Live data from Hacker News

Logic programming is overrated, at least for logic puzzles (2013)

programming-puzzler.blogspot.com

21–30 of 43 posts

Re: Logic programming is overrated, at least for logic puzzles (2013)

#21
post #6

For me, I think the problem is that normal, boring, stupid, unsophisticated, plebian imperative programming lives in a world of O(1) operations. That is to say, a "normal" line of code you type will be O(1), and then you generally start gluing those together with various things that start stacking on O(n) complexities. As things like the accidentally quadratic blog [1], in the normal programming world it is a bit hum…

The issue you have circled around here is a bit deeper and nuanced than trimming down exponential code to something more tractable. In fact, it's often worse, and O(2^n) can be a blessing!

The problem with logic programming is the 'logic' part.

Modern imperative and functional programming languages are constructive.

Logic programming is not, and the expressive power varies with the exact logic being used.

Elementary logic gives you the following intuition. Propositional logic -- easy. First order logic -- easy (with caveats). And (some kinds of) second order logic -- only easy if you get lucky with the problem you are trying to solve.

For logic based programming languages and systems, both the language implementer and the programmer have to be careful about supporting and using language constructs which boil down to tractable computation.

This is much more difficult than it seems like.

For example, when using SMT solvers you learn quickly that multiplication and division with constants is very fast while the same operations with variables can be intractable. i.e. reasoning about x * C --easy, while x * y is often (but not always..) going to hang.

Re: Logic programming is overrated, at least for logic puzzles (2013)

#22
Given a list of numbers L, use operations +,-,*,/ to obtain a result of Result. Use each number of L exactly one time.

  Code:

  j24([X],R,H) :- !, X =:= R,H=X.
  j24(L,R,H) :-  select(X1,L,L1), x2(X1,Op,X2,R),
      j24(L1,X2,H1),H =..[Op,X1,H1],tj24(Op,X1,H1).
   
  tj24(Op,X1,H1) :- member(Op,['+','*']),
     H1 =..[Op,Y1,_],integer(X1),integer(Y1),!,X1 =
Edited: I made a small change to prune solutions:

  x2(X1,'+',X2,R) :- X2 is R - X1, X1 = 0.
  x2(X1,'\*',X2,R) :- X1 =\= 0, X2 is R / X1, X1 =
The program does not compute all the solution. When the parameter Result is relatively small is easier to obtain a problem with many solution, so the algorithm is fast.

The Prolog program computes a solution in one or two seconds, how would be a similar python program to solve this problem in less than 10 lines?

Re: Logic programming is overrated, at least for logic puzzles (2013)

#23
post #6

For me, I think the problem is that normal, boring, stupid, unsophisticated, plebian imperative programming lives in a world of O(1) operations. That is to say, a "normal" line of code you type will be O(1), and then you generally start gluing those together with various things that start stacking on O(n) complexities. As things like the accidentally quadratic blog [1], in the normal programming world it is a bit hum…

This seems right but I think embedded logic programming in the standard lib is way closer to the right solution than expecting to tackle the appropriate tasks with a separate language. Many programs have a sub-problem that is a good fit for logic programming, but it's rare that it be so well encapsulated it's worth deploying an entire separate language for it.

Having these tools close at hand the same way we have regex and (more recently) PEGs for grammars definitely makes it more likely that people will reach for them when appropriate, and exposure and familiarity to the practices can grow.

I suspect part of the problem is just that minikanren is primarily a learning tool and doesn't prioritize practical ergonomics. I haven't used core.logic but I've used minikanren in this embedded way and it's difficult to do cleanly in ways that have nothing to do with logic programming per se. I may be off but I think it's something that having a high quality professional strength implementation in the stdlib could actually help with.

Re: Logic programming is overrated, at least for logic puzzles (2013)

#24
post #6

For me, I think the problem is that normal, boring, stupid, unsophisticated, plebian imperative programming lives in a world of O(1) operations. That is to say, a "normal" line of code you type will be O(1), and then you generally start gluing those together with various things that start stacking on O(n) complexities. As things like the accidentally quadratic blog [1], in the normal programming world it is a bit hum…

Prolog comes from the era (the 70s) where computationally interesting and algorithmically complex problems were a proportionately much larger part of the computing scene than they are today, what with our personal computers, data crunching, and web applications. It’s not really that those kinds of computing paradigms are bad so much as they aren’t nearly as relevant as they were. And a typical programmer, even if they do encounter a problem appropriate for that kind of approach, is much better off using the typical software they have tons of experience with, rather than attempting to learn a new paradigm and uncovering all its pitfalls in the process of learning it.

Exhaustively searching an exponential space with no heuristics for reducing the search space is obviously gonna be way easier in a familiar language than in a new language.

The one use case I do see for prolog and similar tools in the modern day is for Constraint Satisfaction programming. Obviously, exhaustively searching an exponential solution space won’t be better for that, but typically you can use concepts like Edge and Arc consistency (which are core to CSP solvers) to greatly reduce the actual search space. Implementing those algorithms from scratch in an imperative language is annoyingly hard, so reaching for optimized, generalized implementations can be a good choice.

For something like a Sudoku solver, edge/arc consistency can greatly improve compute times. If you’re good at sudoku, you are already implementing these techniques when solving puzzles, just like good chess players implement minimax search without having to be told what minimax search is.

https://www.sciencedirect.com/topics/computer-science/arc-co...

Re: Logic programming is overrated, at least for logic puzzles (2013)

#25
post #11

I’ve written programs to solve and create those grid-style logic puzzles (such as “Five men went to dinner. Mr Brown ordered fish. The man with the red hat ordered lamb. The person who ordered beef was not Mr. White… etc.) Once you have a way of encoding the clues into a machine-readable syntax, it’s trivial to solve. But, the tricky part is creating a good puzzle that can be solved by a human without guessing. That’…

Couple of very good kids games recently on this theme: https://www.thinkfun.com/products/dog-crimes/ and https://www.thinkfun.com/products/cat-crimes/

If you have a suitably interested child, this could provide a fun bridge to constraint based programming.

Re: Logic programming is overrated, at least for logic puzzles (2013)

#26
>> I think a lot of people mistakenly believe that core.logic is working some magic behind the scenes to solve puzzles in some amazingly efficient manner. On the contrary, it's just brute force search, using complex machinery which just slows it down versus a for comprehension.

If that's what core.logic is, then core.logic is not logic programming.

Logic programming refers to one of two things: either Prolog-like languages where programs are sets of definite clauses executed by SLD-Refutation of a goal, and evaluation returns all bindings of the variables in the goal that make the goal true; or Answer Set Programming (ASP), where programs are similar to definite programs, but the semantics are stable model semantics and evaluation finds all the stable models of a program.

Both of these approaches incorporate search, but neither is "just brute force search", in any way, shape or form.

Re: Logic programming is overrated, at least for logic puzzles (2013)

#27
post #18

As pointed out in the comments in the article, these kinds of logic puzzles are easier to solve using constraint programming than "regular" logic programming. For example, see the solution to the Zebra Puzzle here: https://www.metalevel.at/prolog/puzzles which uses CLPZ[^1]. [^1]: https://github.com/triska/clpz

To clarify, CLP((Z) does not stand for "Constraint Logic Programming for the Zebra puzzle"

:P

Re: Logic programming is overrated, at least for logic puzzles (2013)

#28
post #18

As pointed out in the comments in the article, these kinds of logic puzzles are easier to solve using constraint programming than "regular" logic programming. For example, see the solution to the Zebra Puzzle here: https://www.metalevel.at/prolog/puzzles which uses CLPZ[^1]. [^1]: https://github.com/triska/clpz

How is that "easier" than the following straightforward Prolog code?

    zebra(Houses) :-
        houses(Houses),
        member(house(red, english, _, _, _), Houses),
        member(house(_, spanish, dog, _, _), Houses),
        member(house(green, _, _, coffee, _), Houses),
        member(house(_, ukrainian, _, tea, _), Houses),
        right_of(house(green,_,_,_,_),
          house(ivory,_,_,_,_), Houses),
        member(house(_, _, snails, _, winstons), Houses),
        member(house(yellow, _, _, _, kools), Houses),
        Houses = [_, _, house(_, _, _, milk, _), _,_],
        Houses = [house(_, norwegian, _, _, _)|_],
        next_to(house(_,_,_,_,chesterfields),
          house(_,_,fox,_,_), Houses),
        next_to(house(_,_,_,_,kools),
          house(_,_,horse,_,_), Houses),
        member(house(_, _, _, orange_juice, lucky_strikes), Houses),
        member(house(_, japanese, _, _, parliaments), Houses),
        next_to(house(_,norwegian,_,_,_),
          house(blue,_,_,_,_), Houses),
        member(house(_, _, zebra, _, _), Houses),
        member(house(_, _, _, water, _), Houses).

    houses([
        house(_, _, _, _, _),
        house(_, _, _, _, _),
        house(_, _, _, _, _),
        house(_, _, _, _, _),
        house(_, _, _, _, _)]).
    
    right_of(A, B, [B, A | _]).
    right_of(A, B, [_ | Y]) :- right_of(A, B, Y).
    
    next_to(A, B, [A, B | _]).
    next_to(A, B, [B, A | _]).
    next_to(A, B, [_ | Y]) :- next_to(A, B, Y).
    
    member(X, [X|_]).
    member(X, [_|Y]) :- member(X, Y).
    
    ?- zebra(Houses) 
To check it out yourself, copy/paste this into https://quantumprolog.sgml.io/browser-demo/browser-demo.html and execute on your browser.

Re: Logic programming is overrated, at least for logic puzzles (2013)

#29
> But the disadvantages of core.logic for solving logic puzzles don't end there. core.logic is very sensitive to the way that goals are ordered, in a bad way. Certain orderings, for example, will cause the program to go into an infinite loop, and the DSL is complex enough that this is not always readily apparent.

This is just not correct (or at least phrased VERY poorly). My understanding is that core.logic uses Minikanren under the hood which is guaranteed to find an answer if an answer exists regardless of goal ordering. Unlike Prolog, Minikanren (and presumably core.logic) do not search depth first (which is why Prolog can run into infinite loops very easily). Instead, it roughly does interleaving BFS. It can search inefficiently, but that's true of any combinatorial method.

As for practical use cases, I really like how easy it is to write analysis code for parse trees and type systems. The C# T-SQL parser (used in various tools) is very tedious to actually use for quick jobs because of how many different node types it has. Instead of handling that monstrosity, I wrote a much smaller app that just went over the whole tree and converted it to JSON (including the types and type hierarchy) via reflection. It was then way easier to load it into Prolog and query the tree.

Re: Logic programming is overrated, at least for logic puzzles (2013)

#30
post #18

As pointed out in the comments in the article, these kinds of logic puzzles are easier to solve using constraint programming than "regular" logic programming. For example, see the solution to the Zebra Puzzle here: https://www.metalevel.at/prolog/puzzles which uses CLPZ[^1]. [^1]: https://github.com/triska/clpz

How is that "easier" than the following straightforward Prolog code? zebra(Houses) :- houses(Houses), member(house(red, english, _, _, _), Houses), member(house(_, spanish, dog, _, _), Houses), member(house(green, _, _, coffee, _), Houses), member(house(_, ukrainian, _, tea, _), Houses), right_of(house(green,_,_,_,_), house(ivory,_,_,_,_), Houses), member(house(_, _, snails, _, winstons), Houses), member(house(yellow…

  solution(Pairs, Water, Zebra, Vs) :-
          Table   = [Houses,Nations,Drinks,Smokes,Animals],
          Houses  = [Red,Green,Yellow,Blue,Ivory],
          Nations = [England,Spain,Ukraine,Norway,Japan],
          Names   = [england,spain,ukraine,norway,japan],
          Drinks  = [Coffee,Milk,OrangeJuice,Tea,Water],
          Smokes  = [OldGold,Kools,Chesterfield,LuckyStrike,Parliaments],
          Animals = [Dog,Snails,Horse,Fox,Zebra],
          pairs_keys_values(Pairs, Nations, Names),
          maplist(all_distinct, Table),
          append(Table, Vs),
          Vs ins 1..5,
          England #= Red,               % hint 1
          Spain #= Dog,                 % hint 2
          Coffee #= Green,              % hint 3
          Ukraine #= Tea,               % hint 4
          Green #= Ivory + 1,           % hint 5
          OldGold #= Snails,            % hint 6
          Kools #= Yellow,              % hint 7
          Milk #= 3,                    % hint 8
          Norway #= 1,                  % hint 9
          next_to(Chesterfield, Fox),   % hint 10
          next_to(Kools, Horse),        % hint 11
          LuckyStrike #= OrangeJuice,   % hint 12
          Japan #= Parliaments,         % hint 13
          next_to(Norway, Blue).        % hint 14

  next_to(H, N) :- abs(H-N) #= 1.
It's subjective, but the above is far clearer about the rules to me. Compare `next_to` in both:

  next_to(Kools, Horse)
  next_to(house(_,_,_,_,kools),
          house(_,_,horse,_,_), Houses)
The former is much more straightforward as a representation of the hint. You don't need to know which entry in a `house` corresponds to cigarette brand and animal, you can just directly compare the two and say Kools is next to Horse.
Post reply on HN