Hacker News new | past | comments | ask | show | jobs | submit login
Guitar Chord Voicings with Prolog (petecorey.com)
154 points by edavis on April 21, 2020 | hide | past | favorite | 37 comments



Interesting that this intersects with something I've been experimenting with lately. A friend of mine came up with a way of doing 41-tone equal temperament on a modified guitar [1]. One of the tricks is that since 41-TET would require the frets to be so tightly packed that the guitar would be difficult to play, you simply omit every other fret and tune the guitar so that each string has the notes its neighbors on either side lack.

It's a little strange not having all the notes on all the strings, but it actually works out surprisingly well. Chord intonation is a bit nicer than 12-TET, and you get pretty good approximations of the 7-limit just intonation intervals as well.

[1] https://en.xen.wiki/w/The_Kite_Guitar (The 7-string Ibanez is mine.)


There's a similar trick with a normally fretted guitar: tune the strings in alternating major and minor thirds (something like ACEGBD) and lower every other string by 15 cents. Then any three adjacent strings will be a just intonation major or minor triad, you can mini-barre them on any fret to get more triads, and there are just intonation scales in every key.


I haven't heard of that one, but I have heard of simpler tricks like re-tuning one string so the third of certain barre chords comes out right. There's always a bit of a trade-off; some intervals get better and others get worse so in effect you're optimizing the instrument for a particular playing style or key or song.

If you're willing to modify the guitar to put frets in different places, it allows for a lot of options you wouldn't have otherwise. (This is especially true if you're willing to use partial frets.)

Kite tuning is the best scheme I've encountered for having closer-to-just intervals than 12-TET and yet you can play in any key. If you want perfect intervals, plain just intonation is better but then you're locked into a particular key and scale.

The design space of guitar fingerboards is surprisingly huge and open-ended when you stray even a little from the twelfth root of two as the fundamental unit of musical interval width.


Once, when I was taking a music theory class, I tried to use the Z3 constraint solver to automatically harmonize melodies… it worked surprisingly well! https://github.com/kach/recreational-rosette/tree/master/mus...


Always interesting to see logic programming applied to music, it is in my experience a good fit for the musical problem space. I spent some time exploring the analysis of chords using answer set programming. On the surface ASP programs look very similar to Prolog but the semantics differ; you query Prolog to answer a question or series of questions related to your program, whereas ASP produces complete "stable models" of your program (it answers "all questions").

The first version of my analysis program would most likely have been a valid Prolog program, and furthermore the program was completely positive (contained no negation). After several weeks of rewrites, I ended up with a much smaller program that also took advantage of ASP "choice" and "optimization" rules. Much of the complexity was reduced by one fundamental improvement in the representation of internal facts and adjusting the rules to accommodate. In hindsight, the original version used a rather foolish approach, but like many things it took some effort to realize why and how to do better.

This process of rewriting and incremental improvement (and initially struggling in a less-familiar programming paradigm) gave me a great appreciation for logic systems, and also a desire to use them more often. There is something very beautiful in reducing a problem into logical relationships, and instead of focusing on algorithms just focusing on the problem space and how different pieces interrelate. It does take practice and experimentation to produce an elegant solution to a problem, but with experience it gets easier. I have no doubt the author of this article could make it feasible to discover chords and tunings as he describes in the Future Work section. Though I would recommend starting over with this goal in mind; even a naive but viable solution might reveal some property of the system that would transfer over to the original program and increase its versatility.


I'd love to see that code. Sounds awesome.


I intend to do a proper writeup for this, in the meantime I published a short blog[1] with the program along with some background information.

[1] https://blog.phazon.xyz/answer-set-programming/2020/04/22/mu...


This is great, thanks for posting! I'll definitely take your advice to heart. I've been noticing that finding different data representations of the problem at hand is a big part of finding the "core relations" that describe the problem. My first attempt at the voicing/4 predicate was attempted with a data structure used in a procedural version of the algorithm, and I just couldn't get it to work in Prolog. The second iteration is what you've seen, and I'm working on a third iteration with a simpler data representation that is simplifying the problem even further.


Thank you!


Tangentially related; I wrote some python code to come up with every possible pentatonic scale (including some restrictions for musicality) - If nothing else, I learnt a lot about scales and how they relate to one another

https://github.com/ValdemarOrn/PentatonicScales


So I've been interested in learning prolog ever since I found out it was a language used for AI. I am wondering if it is worth picking up now?

Does it change the way one things about interacting with a computer or a problem?

Is it something that will soon grow?


Prolog is fundamentally well established (~40 years old), and you can start using it today in several programming languages. For example:

JavaScript http://tau-prolog.org

Ruby https://github.com/preston/ruby-prolog

Rust https://github.com/mthom/scryer-prolog

Erlang https://github.com/rvirding/erlog

If your language is missing one, implementing a basic Prolog is fairly simple. Take a look at the Ruby one: it's core is not even 500 lines of code!

There are also great options like SWI Prolog if you want to build your entire app in Prolog. But I imagine it would be much easier to convince a work boss to pull in a library instead :)

Re AI: It's true that Prolog's origins involved AI, but nowadays Prolog is better suited for more general logic programming tasks (there are better tools out there for AI specifically).


> Erlang https://github.com/rvirding/erlog

Fun fact: Erlang started out as a modified version of Prolog. The original Erlang VM was written in Prolog as well.


Prolog is a general purpose programming language. You can use it to implement provers, optimization problem solvers, or generally use it as a unification algorithm engine and database.

You could say prolog has branched many ways. There are many similar languages now which take some aspect prolog did well and made it better. Problog, Mercury, Picat, logtalk, Allego Graph prolog

There's also datalog, which is like the logic database subset of prolog. There's datomic, datahike, datascript, dlv, abcdatalog, racket datalog.

Then there's the RDF / Semantic web side, which feels a lot like datalog. There's many reasoners for triple stores which work like logic databases. RDFox is also a bleeding edge datalog implementation for incremental materialization, you can use it as a business rules engine as well as a database, it keeps recursively derived facts always up to date even as you add new facts. The biggest triple store implementation is probably wikidata.

AI / ML these days try to find patterns in data and reproduce those patterns reliably. Many of the use cases with prolog and prolog-like languages are more about feeding in known facts, and deriving more facts, implications, putting models that represent known and relationships and rules between facts and hierarchical state. Who ever finally makes an ML capable of general intelligence, I'm willing to bet they will interface neural network technologies with a modifiable fact database capable of generating models for the rules and relationships between facts and the hierarchy of those facts.

Probably a lot of this declarative yaml state used in devops these days could benefit from prolog-like logic for drift detection, alerts and recovering broken configurations automatically. Any type of big data DAG that has constraints of complex implications of specific paths can benefit from logic programming. If you have a complex knowledge base like a legal corpus, it can help massively narrow down research needed by asking simple questions that half the search space each time. People have used it for NLP for it's pattern matching abilities. You can reuse the same function against multiple patterns.


> Does it change the way one things about interacting with a computer or a problem?

Yes, I think so. Even is you just spend an evening going through http://www.learnprolognow.org/ that's a gain.

> Is it something that will soon grow?

I have no reason to expect that.


The last two Prolog 8 week intensive classes put on by the SWI-Prolog community have taught more than 400 people modern Prolog, so yes its growing and depending on what kinds of problems you're dealing with it may be worth picking up now.

Like most tools that give companies a serious competitive advantage they aren't really talking about it.

Does it change the way you think about programming?

Definitely.

Prolog gives me the tools to solve the right problem the right way in ways I'd be scared to attempt in other languages.

Consider the following scenario. You run a forum. It's super succesful. You now need to implement access control.

In most languages I think white lists. Then you need to add a blacklist, then some sort of inheritance, then its a mess.

In Prolog going straight for role based access is 50 lines of code. Tops.


> Is it something that will soon grow?

Yes, like a tree.

It took me twenty years to notice Prolog. I wish I had picked it up twenty years ago.


My guess is that Prolog was more of an AI thing back when people were doing symbolic AI (including Lisp), instead of the statistical shotgun it's latterly become. Prolog is very good at NLP programming, and BNF grammars, and those things were important to an earlier approach to AI.


I am sure they will come back, probably in combination with other recent techniques. We still need an AI that understands what it's doing. Disclaimer: I'm an ignoramus.


I kinda want to see a riff generator, searchable by chord base/tonality, time signature, and length. Also a scale exercise generator. Lot of space to explore in terms of permutations. I seem to recall some research on probability of usage of a particular set of notes/chords based on information entropy but I can't find that anywhere.


It's an interesting problem

An even harder problem is figuring out the chord corresponding to a given set of notes. And it is surprisingly hard (though humans seem to do it "easily" (with experience).

Even harder because for some there is no right answer, or better, the right answer is the simplest. Yes C-E-G is a CMajor chord, but what is D-F-Bb-E ?


I'm working n an IDE for music composition. Launching soon. http://ngrid.com


Redirects to National Grid US.



Just so you know - you can edit your previous post.


You can't after 2 hours.


TIL : Today I learned. I didn't know that.

Although I have made your kind of mistake before.

This is why I do a post-proof read / link test after I post.


If you mix up your own website, you ought to consider rebranding.


I'm considering it.


Re: Future Work: I didn’t think about the particular predicates in detail, but the author could consider using the cut predicate (!) to prevent some unnecessary backtracking. Without more thought I can’t guarantee that actually could be effectively applied here, though


Prolog is rather limiting; that's why Lisp has established itself in traditional AI and especially in algorithmic music composition/generation too (see e.g. Experiments in Musical Intelligence, EMI, or OpenMusic or CLM); even today there are very interesting tools based on Lisp for music generation, e.g. https://en.wikipedia.org/wiki/Impromptu_(programming_environ....


> Prolog is rather limiting;

How is it limiting?


Lisp is a general purpose programming language, supports any paradigm and the implementation of domain specific extensions. Tool and library support is very good and there is abundance of tutorials, books and people supporting it. Prolog is not a general purpos programming language, but you have to write relations and queries which are then resolved by SLD. You can implement that in Lisp and it runs very fast e.g. on SBCL which has a JIT compiler; I'm not aware of similar performance with Prolog compilers. Just to name a few limitations.


Thanks for your response.

I love Lisps (especially Racket and Clojure) so let me just be clear that I am not going to argue that one is "better" than the other.

> library support is very good

That depends mostly on the implementation. SWI Prolog has a decent number of libraries available. Tau Prolog can interoperate with JavaScript.

> Prolog is not a general purpos programming language

I've made a HN clone in SWI Prolog. You can represent HTML as lists, like you would in many Lisps. In some sense, Prolog can occasionally feel more general purpose because you don't need a separate language (like SQL) for your database.

> but you have to write relations and queries which are then resolved by SLD.

That is true, yet in some sense this is like stating about Scheme that "you have to write functions which are then evaluated". Yep, you have to do that but it doesn't mean the language can't be used for X.

> there is abundance of tutorials, books and people supporting it.

Yup, I dig SICP, The Little Schemer and Rich Hickey's talks. When it comes to Prolog, Markus Triska's presentations are amazing[0].

If it was all about which language has the most books written about it, I'd worry that the "winner" would be Java. (I haven't particularly enjoyed the books I've read about Java though.)

> supports any paradigm

I'm not sure that is necessarily a good thing. JavaScript kinda does this, and I feel like I can never tell if a function or method call is pure or mutates something. Does `foo.sort()` modify `foo` itself, or does it just return an a sorted collection of the elements in `foo`?[1]

I also find it confusing in Common Lisp that `sort` is destructive[2]. I think Clojure fares better there by sticking to immutability.

[0]: https://www.metalevel.at/prolog/videos/

[1]: https://medium.com/@winwardo/the-principle-of-least-astonish...

[2]: http://clhs.lisp.se/Body/f_sort_.htm


sort being likewise destructive in TXR Lisp is a bit of a regret of mine; we can easily have a nsort (non-consing sort) be destructive, and sort being equivalent to (nsort (copy obj) ...).

I think that the designers get seduced by the fact that we have such good techniques for in-place sorting that the idea of sorting becomes synonymous with those techniques.

You would never implement a non-destructive sort first and then add an accompanying destructive one; the non-destructive one is the afterthought.

The Javascript blog doesn't really nail things here. It's perfectly legitimate to return "this" and mutate, and possibly useful. For instance, we could have an accumulator object such that:

  accumulator(42).add(1).add(1).sub(4)
ends up with 40. Returning the object allows for threading through expressions like this, which works imperatively also.

In the case of Lisp, the destructive sort must return a value that is captured, because if the object is a list, and that list is sorted by a re-arrangement of the cons cells, the original list reference is quite likely longer a reference to the entire list.

Returning a value that the caller must capture also allows the operation to be specified as being destructive or not. If the caller never touches the input object after passing it into the operation, and just uses the returned one, it cannot tell.

Returning a value also allows for a succinct fix for broken code. If the sort was not intended to be destructive in some (f (sort x) y) expression, you just insert (f (sort (copy x)) y) instead of a verbose workaround like (let ((xc (copy x)) (f xc y)). Likewise in the accumulator case, if we want to avoid mutating some original accumulator, we could have a copy method:

  var new_acc = old_acc.copy().add(1).mul(2) // old_acc stays 42, new_acc is 86.


> Prolog can occasionally feel more general purpose

That's subjective; Prolog is/was not intended to be a general purpose programming language, as little as e.g. SQL. And human imagination and perseverance make it possible to reach the goal even under the most adverse circumstances. If you make an effort, you can also build an HN clone in Assembler or Brainf*ck. You cannot conclude from this that the language is intended/suited for this.

> yet in some sense this is like stating about Scheme that "you have to write functions which are then evaluated"

No. There are different ways to evaluate rules. If you don't like SLD then the tinkering begins if you want to stay in Prolog.

> I'm not sure that is necessarily a good thing

It is. E.g. MOP in Lisp is something extremely powerful. With macros you can do things that languages like JavaScript (or Prologue) can only dream of; but you know that yourself for sure.


Lol, it's nothing like assembler or brainfuck.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: