Live data from Hacker News

Discovering algorithms by enumerating terms in Haskell

twitter.com

21–30 of 50 posts

Re: Discovering algorithms by enumerating terms in Haskell

#21

Uhm author here. Not sure why this tweet is on Hacker News, as it is just a non-technical "blog post". But I've posted a follow-up today with some code and details, if you're curious: https://x.com/VictorTaelin/status/1819774880130158663 That's as much as I can share for now though

Only thing I could read (don't understand any of this otherwise) was that you take input pairs and you give python function to generate that output. Does that mean that many math etc problems can be just solved? What kind of python code will it generate to return primes?

It will just return the smallest function that passes your tests.

It works by enumerating ALL possible functions and running them. Obviously, that naive approach is exponential, so, the entire point is whether we can apply some clever tricks (based on optimal evaluators) to make this search "slightly less intractable".

So, to answer your question directly: if you asked it to design a prime number generator, if it found anything at all, it would probably be a simple, slow trial-and-error algorithm, or something in these lines.

Re: Discovering algorithms by enumerating terms in Haskell

#22

I'm confused, what is the search space? All possible functions from Int -> Int? And how do you verify a result is optimal across inputs?

The search space I'm using is that of all functions of a given dependent type. That allows you to make the search space by using a strong enough type.

For example, if you search for `Integer -> Integer -> Integer` function, it will consider Integers of different bit-sizes. But if you instead search for `∀(n: Nat). Int(n) -> Int(n) -> Int(n)`, you will only consider integers of the same bit-size, which is a much smaller space. You can make arbitrary restrictions to shorten your search.

Re: Discovering algorithms by enumerating terms in Haskell

#23
post #18
post #13

Maciej Bendkowski has some related work [1] on generating random lambda terms, but was unable to overcome what he calls the asymptotic sparsity problem: Sampling simply-typed terms seems notoriously more challenging than sampling closed ones. Even rejection sampling, whenever applicable, admits serious limitations due to the imminent asymptotic sparsity problem — asymptotically almost no term, be it either plain or c…

There are quite a few publications that explore the concept of generating programs, either using typed or untyped functional languages. For example, wake-sleep learning and NN on a Lisp-like language [1] or synthesis of Haskell guided by refinement types [2]. [1] https://royalsocietypublishing.org/doi/full/10.1098/rsta.202... . [2] https://dl.acm.org/doi/abs/10.1145/2980983.2908093

The trick is not just synthesizing valid functions, but doing so in a parallel communication-free manner, without compromising soundness or completeness. You want to massively scale up a discrete sampler without replacement. One very efficient way of doing this is by constructing an explicit bijection from the sample space to the integers, sampling integers, then decoding them into programs.

While this technique enjoys certain advantages, i.e., it is embarrassingly parallelizable and guaranteed to enumerate distinct solutions with a bounded delay, it also somewhat unnatural. By flattening the distribution onto the integers a la Gödel numbering, it destroys locality, does not play well with incremental decoding methods (left-to-right is currently en vogue in generative language modeling), and will fail if the sample space is uncountable.

Another key step is reducing symmetries in your sample space by quotienting it somehow (e.g., by α-equivalence). The author seems to be invoking some kind of equivalence relation by “superposition”, but the technical details here are a little fuzzy.

This problem is also closely related to model counting in the CSP literature, so a practical speedup could lead to improvements on a lot of interesting downstream benchmarks.

In general, the problem of program induction from input-output examples is not well-posed, so specialized solvers that can make stronger assumptions will usually have an advantage on domain-specific benchmarks. Most existing program synthesizers do not satisfy all of these desiderata (e.g., soundness, completeness, naturalness, incrementality).

Re: Discovering algorithms by enumerating terms in Haskell

#25
What the author is getting at is a pretty cool research area called program synthesis, where the goal is to create a program that satisfies a specification.

Most techniques are essentially brute force enumeration with tricks to improve performance, so they tend to struggle to find larger programs. A lot of active research is in improving performance.

Compared to asking a LLM to write a program, program synthesis approaches will guarantee that a solution will satisfy the specification which can be very powerful.

In particular, as the author has discovered, one area where program synthesis excels is finding small intricate bitwise operator heavy programs that can be hard to reason about as a human.

The most famous example of program synthesis is Microsoft's FlashFill, which is used in Excel. You give it a few input output examples and FlashFill will try to create a small program to generalize them, and you can apply the program to more inputs, which saves you a bunch of time. An example from the paper is:

  Input -> Output

  International Business Machines -> IBM
  Principles Of Programming Languages -> POPL
  International Conference on Software Engineering -> ICSE

  String Program: Loop(\w : Concatenate(SubStr2(v_1, UpperTok, w)))

Here's a few papers:

EUSOLVER: https://www.cis.upenn.edu/~alur/Tacas17.pdf

FlashFill: https://www.microsoft.com/en-us/research/wp-content/uploads/...

BlinkFill: https://www.vldb.org/pvldb/vol9/p816-singh.pdf

Synquid: https://cseweb.ucsd.edu/~npolikarpova/publications/pldi16.pd...

Re: Discovering algorithms by enumerating terms in Haskell

#26

I'm confused, what is the search space? All possible functions from Int -> Int? And how do you verify a result is optimal across inputs?

The search space I'm using is that of all functions of a given dependent type. That allows you to make the search space by using a strong enough type. For example, if you search for `Integer -> Integer -> Integer` function, it will consider Integers of different bit-sizes. But if you instead search for `∀(n: Nat). Int(n) -> Int(n) -> Int(n)`, you will only consider integers of the same bit-size, which is a much small…

So you're enumerating a search space of functions, with some constraints to keep the search tractable?

When you do this kind of thing, there are two worst cases:

a) Your constraints are too strong and the search space does not include the target.

b) The search space includes the target but it is too large to search in polynomial time.

How are you dealing with those?

To clarify, the happy case is when your search target is easy to find, i.e. when the search space is small and includes the target. But that happens... rarely (because program search spaces are large).

Re: Discovering algorithms by enumerating terms in Haskell

#27
post #14

This kind of reminds me of what happens when you implement an interpreter in a relational programming language, which lets you do cool stuff like generating quines by specifying that the program and it’s output should be the same. Quick search lead to this paper which is exactly what I’m talking about: http://webyrd.net/quines/quines.pdf

William Byrd is the creator of miniKanren btw. And an HN user.

Re: Discovering algorithms by enumerating terms in Haskell

#29

Uhm author here. Not sure why this tweet is on Hacker News, as it is just a non-technical "blog post". But I've posted a follow-up today with some code and details, if you're curious: https://x.com/VictorTaelin/status/1819774880130158663 That's as much as I can share for now though

Hi LightMachine. I can't read Haskel (?). In this screenshot:

https://x.com/VictorTaelin/status/1819208143638831404/photo/...

What's the highlighted program (?) on line 1875997?

Re: Discovering algorithms by enumerating terms in Haskell

#30
post #25

What the author is getting at is a pretty cool research area called program synthesis, where the goal is to create a program that satisfies a specification. Most techniques are essentially brute force enumeration with tricks to improve performance, so they tend to struggle to find larger programs. A lot of active research is in improving performance. Compared to asking a LLM to write a program, program synthesis appr…

Note that FlashFill is an example of inductive program synthesis, i.e. program synthesis from an incomplete specification, e.g. one in the form of input-output examples, program traces, or natural language descriptions.

Program synthesis from a complete specification is known as deductive program synthesis and the simplest example is compilation of a program in a high-level language to a machine code. A "complete" specification is what it says on the tin: it fully specifies the program to be synthesised.

Post reply on HN