├── .gitignore ├── LICENSE ├── README.md ├── advanced.adoc ├── assets ├── gradstyle.css ├── kumquat.jpg ├── specialstyle.css └── swipl.png ├── basics.adoc ├── cellautomata.pl ├── chr_experiments.pl ├── chrtest.pl ├── constraintsystems.adoc ├── dev ├── addfooter.ai ├── blockdiagramlayout.graphml ├── comfy.ai ├── comfymodule.ai ├── systemsusingprolog.psd └── twocamps.ai ├── examples.adoc ├── examples └── techtree.pl ├── final.adoc ├── house.png ├── housebody.png ├── iiCHRpaper.pdf ├── images ├── Robert_Wadlow.jpg ├── automaton.png ├── automatona.png └── icons │ ├── callouts │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png │ ├── caution.png │ ├── example.png │ ├── home.png │ ├── important.png │ ├── next.png │ ├── note.png │ ├── prev.png │ ├── tip.png │ ├── up.png │ └── warning.png ├── index.adoc ├── makehtml.sh ├── moduleb.pl ├── pathways ├── pathways.css ├── style.css └── toc2.css ├── sources.txt ├── style.css └── unusedexamples.adoc /.gitignore: -------------------------------------------------------------------------------- 1 | index.html 2 | *.*~ 3 | **/*.*~ 4 | *~ 5 | **/*~ 6 | *.html 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Author: Anne Ogborn 2 | E-mail: annie@theelginworks.com 3 | WWW: http://theelginworks.com 4 | Copyright (c) 2018, Anne Ogborn 5 | 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions 10 | are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | 15 | 2. Redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in 17 | the documentation and/or other materials provided with the 18 | distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SWI-Prolog CHR tutorial 2 | 3 | This is [a tutorial that teaches Thom Frühwirth's CHR system](index.adoc) that comes as part of the standard **SWI-Prolog 4 | distribution. 5 | 6 | ## Install 7 | 8 | Install asciidoctor 9 | 10 | make a directory /etc/asciidoc/themes/pathways 11 | 12 | copy the contents of the pathways directory under the project root into this dir 13 | 14 | 15 | ## Building 16 | 17 | to rebuild the web pages, compile all .adoc files with asciidoc 18 | 19 | ```` 20 | asciidoc -a stylesheet=themes/pathways/style.css -a stylesdir=themes/pathways/ final.adoc >final.html 21 | ```` 22 | 23 | and serve. 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /advanced.adoc: -------------------------------------------------------------------------------- 1 | Tutorial - CHR Constraint Handling Rules - Modes, Types, Performance 2 | ==================================================================== 3 | Anne Ogborn 4 | :Author Initials: AO 5 | :toc2: 6 | :icons: 7 | :numbered: 8 | :website: http://www.pathwayslms.com/swipltuts/ 9 | :theme: pathways 10 | 11 | 12 | link:/swipltuts/index.html[Up to All Tutorials] 13 | link:index.html[Introduction] 14 | link:basics.html[Basics] 15 | link:examples.html[Examples] 16 | link:constraintsystems.html[Constraint Systems] 17 | link:advanced.html[Advanced] 18 | link:final.html[Final] 19 | 20 | This chapter covers some advanced CHR topics. 21 | 22 | Much of the material in this chapter is taken from the 23 | link:https://dtai.cs.kuleuven.be/CHR/files/CHR_cheatsheet.pdf[CHR Cheatsheet] by Amira Zaki, Thom Frühwirth, and Jon Sneyers. 24 | 25 | Because this document covers much of what I want to cover in this chapter better than I could, 26 | I'll be referring the student to it repeatedly. 27 | 28 | Advanced CHR 29 | * CHR options 30 | * modes and types 31 | * pragmas 32 | * modules 33 | * performance 34 | * tools 35 | 36 | CHR Options 37 | ----------- 38 | 39 | You can set CHR options with the directive `chr_option/2`. Options are 40 | 41 | * `check_guard_bindings` (on/off) - make guards that bind variables error the CHR compiler (otherwise their behavior is undefined). 42 | * `optimize` (full/off) - do optimization during compilation (which disables debugging) 43 | * `debug` (on/off) - generate debug information (which carries a performance penalty) 44 | 45 | See CHR Cheatsheet for more info. 46 | 47 | Modes and Types 48 | --------------- 49 | 50 | Providing **mode** (in, out, either + - ?) and **type** information can speed up CHR performance, and provides type checking for program correctness. 51 | 52 | The CHR Cheatsheet coverage of this material is not as good as the 53 | link:https://www.swi-prolog.org/pldoc/man?section=practical[CHR page on swi-prolog.org] 54 | . I suggest reading that first, particularly the section on types and modes, and only then read the material in the CHR Cheatsheet. 55 | 56 | User defined types in particular, and the consequences of defining mode and type, are best described by the SWI-Prolog documentation. 57 | 58 | This material is important both for performance and for getting both static and dynamic type checking. 59 | 60 | [Exercise] 61 | .Exercise - Modes and Types 62 | ===================================================================== 63 | Take several of the CHR programs you will have accumulated by now 64 | and define the CHR constraints with mode and type definitions. 65 | Measure performance on large data sets before and after. 66 | 67 | Include at least one use of `chr_type/1`. 68 | ===================================================================== 69 | 70 | Pragmas 71 | ------- 72 | 73 | The student is referred to the CHR cheatsheet for this material. My experience has 74 | been that adding pragmas is a black art. The CHR mailing list may be your best resource 75 | for doing this efficiently. 76 | 77 | Modules 78 | ------- 79 | 80 | So far we've not discussed modules. 81 | 82 | There is one store per thread, shared by all modules. 83 | 84 | Each **module** is treated independently as far as the **rules** it sees, 85 | and what is allowed in the **head** of the rules. 86 | 87 | However, you can add CHR constraints from other modules, calling them in Prolog or CHR bodies. 88 | 89 | When a CHR constraint is defined in a module, it is module qualified. 90 | 91 | The usual module/use_module/export pattern works: 92 | 93 | in `modulea.pl` 94 | ---- 95 | :- use_module(moduleb). 96 | :- use_module(library(chr)). 97 | 98 | :- chr_constraint in_a/0. 99 | 100 | in_a ==> in_b. 101 | 102 | :- chr_constraint call_private/0. 103 | 104 | call_private ==> moduleb:private_b. 105 | ---- 106 | 107 | in `moduleb.pl` 108 | ---- 109 | :- module(moduleb, [in_b/0]). 110 | 111 | :- use_module(library(chr)). 112 | 113 | :-chr_constraint in_b/0, private_b/0. 114 | 115 | in_b ==> writeln('made in_b'). 116 | 117 | private_b ==> writeln('in private b'). 118 | ---- 119 | 120 | The predicate `find_chr_constraint` doesn't handle modules. Use 121 | link:https://www.swi-prolog.org/pldoc/doc_for?object=chr_runtime%3Acurrent_chr_constraint/1[`current_chr_constraint`] 122 | 123 | Performance 124 | ----------- 125 | 126 | [Exercise] 127 | .Exercise - performance measurement 128 | ===================================================================== 129 | By now you will have accumulated a number of CHR programs as exercises. 130 | 131 | Choose some that can operate on an arbitrary amount of data and greatly 132 | increase that amount. Try running them inside `time/1`. Also try running 133 | the profiler. 134 | ===================================================================== 135 | 136 | Use Modes and Types 137 | ~~~~~~~~~~~~~~~~~~~ 138 | 139 | Alan Baljeu provided this snippet: 140 | 141 | ---- 142 | fc(X) :- 143 | functor(X, XF, N), 144 | functor(Y, XF, N), 145 | find_chr_constraint(Y), % use current_chr_constraint instead in new code 146 | subsumer(X, Y). 147 | 148 | subsumer(A, B) :- 149 | copy_term(B, BCopy) 150 | , catch(A = B, _, fail) 151 | , =@=(B, BCopy) 152 | . 153 | 154 | ?- fc(my_con(A, 3, B)), 155 | fc(my_con(B, 3, C)). 156 | ---- 157 | 158 | It runs in O(n^2) if you define the CHR constraint 159 | 160 | ---- 161 | :- chr_constraint my_con/3. 162 | ---- 163 | 164 | If you instead do this, it is linear. 165 | ---- 166 | :- chr_constraint my_con(+dense_int, +dense_int, +dense_int). 167 | ---- 168 | 169 | Avoid Having Many Heads 170 | ~~~~~~~~~~~~~~~~~~~~~~~ 171 | 172 | Consider this constraint system that looks for a _straight_ in poker. 173 | Assume the face cards are represented by integers: 174 | 175 | ---- 176 | :- chr_constraint card/1, straight/0. 177 | 178 | card(A), card(B), card(C), card(D), card(E) ==> 179 | succ(A,B), 180 | succ(B,C), 181 | succ(C,D), 182 | succ(D,E) | straight. 183 | ---- 184 | 185 | This apparently reasonable constraint operates VERY slowly. If you have 7 cards, as in _7 card stud_, it will arrange the cards in **2,520 ways** and rerun the guard for each, since card(A) can be any of 7 cards, card(B) any of the remaining 6, and so on. 186 | 187 | ---- 188 | ?- time(card(2)),card(4),card(3),card(5),time(card(6)). 189 | % 39 inferences, 0.000 CPU in 0.000 seconds (95% CPU, 1874459 Lips) 190 | % 8,378 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 9221480 Lips) 191 | ---- 192 | 193 | Here's a faster version: 194 | 195 | ---- 196 | :- chr_constraint adj_pair/2, adj_triple/2. 197 | :- chr_constraint card/1, straight/0. 198 | 199 | card(A), card(B) ==> succ(A,B) | adj_pair(A,B). 200 | adj_pair(A,B),card(C) ==> succ(B,C) | adj_triple(A, C). 201 | adj_pair(_,B),adj_triple(C,_) <=> succ(B,C) | straight. 202 | adj_triple(_,B),adj_pair(C,_) <=> succ(B,C) | straight. 203 | ---- 204 | 205 | It runs significantly faster: 206 | 207 | ---- 208 | ?- time(card(2)),card(4),card(3),card(5),time(card(6)). 209 | % 81 inferences, 0.000 CPU in 0.000 seconds (95% CPU, 842854 Lips) 210 | % 527 inferences, 0.000 CPU in 0.000 seconds (99% CPU, 2187530 Lips) 211 | ---- 212 | 213 | Don't Use CHR if Prolog Will Do 214 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 215 | 216 | The CHR compiler is efficient for an automatic code generator, but there is 217 | an inherent performance overhead in using it. 218 | 219 | [Exercise] 220 | .Exercise - Look at the generated code 221 | ===================================================================== 222 | The CHR system produces a Prolog predicate for each chr_constraint. 223 | 224 | Use `listing/1` to look at the code generated by some chr_constraints. 225 | 226 | On my machine `adj_triple/2` from the last section expands to 22 lines 227 | of Prolog in a single clause. 228 | ===================================================================== 229 | 230 | The lesson to be learned is that there is a performance cost to using CHR to do tasks Prolog could do simply. 231 | 232 | Adjust Which arguments are Indexed 233 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 234 | 235 | Section 1.3 of the CHR Cheatsheet describes manually tweaking argument indexing. If you 236 | get this working, you're doing better than the author of this tutorial, and she would 237 | beg you to please explain it to her. 238 | 239 | One strong lesson to be gleaned from the section is that `dense_int` is much faster, particularly 240 | on SWI-Prolog, and it's better to map your data into a dense subset of integers instead of 241 | storing atoms. 242 | 243 | Tools 244 | ----- 245 | 246 | There are some useful tools available for CHR. 247 | 248 | link:http://chr.informatik.uni-ulm.de/~webchr/[WebCHR] 249 | is useful for experimenting with CHR, and has some more examples. 250 | 251 | At times CHR's single threaded nature can be painful. 252 | link:https://github.com/fnogatz/CHR-Constraint-Server[Falco Nogatz] 253 | has created a server that single threads CHR calls. 254 | 255 | For http applications, it might be better to start with the 256 | link:https://github.com/SWI-PrologTeamLudumDare32/LudumDare45[Ludum Dare Team 45 Server] 257 | largely copied from Falco's, but turned into an HTTP server. 258 | 259 | The official 260 | link:https://dtai.cs.kuleuven.be/CHR/[CHR Website] is a resource for both information and tools. 261 | 262 | Some of these tools use the optional **name** you can apply to CHR rules: 263 | 264 | ---- 265 | my_rule @ foo <=> bar. 266 | ---- 267 | 268 | Using the CHR debugger 269 | ~~~~~~~~~~~~~~~~~~~~~~ 270 | 271 | The SWI graphical debugger interacts poorly with CHR, displaying the compiled code. 272 | 273 | The text based 274 | link:https://www.swi-prolog.org/pldoc/man?section=debugging[CHR debugger] is fairly straightforward. 275 | 276 | I never found the _leashing_ options that useful with the Prolog text debugger, but they are quite useful with the CHR debugger. 277 | 278 | This handy pattern prints out the execution of CHR without stopping. 279 | 280 | ---- 281 | % print out execution without pausing 282 | ?- chr_leash(none), chr_trace, query(2,3,N). 283 | ---- 284 | 285 | Simple Tools 286 | ~~~~~~~~~~~~ 287 | 288 | I also find it useful to have some simple tools in my startup. 289 | 290 | ---- 291 | 292 | /******************************* 293 | * helpful utilities * 294 | *******************************/ 295 | 296 | % print out the constraint store 297 | ps :- 298 | current_chr_constraint(Module:Name), 299 | format('constraint store contains ~w:~w~n', [Module, Name]), 300 | fail. 301 | ps. 302 | 303 | % print out constraint store when you return to top level 304 | ss :- set_prolog_flag(chr_toplevel_show_store, true). 305 | 306 | % or don't 307 | noss :- set_prolog_flag(chr_toplevel_show_store, false). 308 | ---- 309 | 310 | An easy way to switch between production and debug (see above in 'CHR Options') is 311 | useful for debug. 312 | 313 | Top Level Removes Residual Constraints 314 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 315 | 316 | CHR removes all residual constraints when you return to the top level. 317 | This can be annoying when trying to debug or program at the interactor. 318 | 319 | SWI-Prolog has a special toplevel mode just for preventing this: 320 | 321 | ---- 322 | % saner way to do same 323 | ?- set_prolog_flag(toplevel_mode, recursive). 324 | ---- 325 | 326 | An alternative to this is Falco Nogatz's 327 | link:https://github.com/fnogatz/CHR-Constraint-Store[CHR Constraint Store] 328 | that provides a CHR repl. 329 | 330 | CHR to JS compiler 331 | ~~~~~~~~~~~~~~~~~~ 332 | 333 | Falco Nogatz has a 334 | link:https://github.com/fnogatz/CHR.js[CHR to JavaScript compiler] 335 | . 336 | 337 | There is a 338 | link:http://chrjs.net/[website for this project] 339 | . 340 | 341 | CHR to SQL compiler 342 | ~~~~~~~~~~~~~~~~~~~ 343 | 344 | A similar compiler exists that 345 | link:https://github.com/awto/chr2sql[converts CHR to SQL] 346 | . 347 | 348 | Conclusion 349 | ---------- 350 | 351 | Here's a truly advanced CHR trick. CHR is available embedded in many languages. 352 | 353 | Since CHR **looks** more like a library than a language, your day job may have less resistance. 354 | 355 | Once you're using CHR, well, the camel's got it's nose in the tent. 356 | 357 | On to the 358 | link:final.html[Final Section] 359 | 360 | 361 | 362 | -------------------------------------------------------------------------------- /assets/gradstyle.css: -------------------------------------------------------------------------------- 1 | p { 2 | border: 14px dotted red; 3 | } -------------------------------------------------------------------------------- /assets/kumquat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/assets/kumquat.jpg -------------------------------------------------------------------------------- /assets/specialstyle.css: -------------------------------------------------------------------------------- 1 | p { 2 | color: #00FF00; 3 | } -------------------------------------------------------------------------------- /assets/swipl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/assets/swipl.png -------------------------------------------------------------------------------- /basics.adoc: -------------------------------------------------------------------------------- 1 | Tutorial - CHR Constraint Handling Rules - Basics 2 | ================================================= 3 | Anne Ogborn 4 | :Author Initials: AO 5 | :toc2: 6 | :icons: 7 | :numbered: 8 | :website: http://www.pathwayslms.com/swipltuts/ 9 | :theme: pathways 10 | 11 | 12 | link:/swipltuts/index.html[Up to All Tutorials] 13 | link:index.html[Introduction] 14 | link:basics.html[Basics] 15 | link:examples.html[Examples] 16 | link:constraintsystems.html[Constraint Systems] 17 | link:advanced.html[Advanced] 18 | link:final.html[Final] 19 | 20 | Contents 21 | -------- 22 | 23 | .The constraint store 24 | .Arguments 25 | .Defining CHR constraints 26 | .Basic CHR syntax 27 | .Making CHR interact with Prolog 28 | .Threads 29 | ** Getting 30 | ** Helpful Utilities 31 | 32 | Basics 33 | ------ 34 | 35 | Here's our _salt water_ example again: 36 | 37 | ---- 38 | :- use_module(library(chr)). <1> 39 | 40 | :- chr_constraint salt/0, water/0, salt_water/0. <2> 41 | 42 | salt,water <=> salt_water. <3> 43 | 44 | ?- salt, water. <4> 45 | salt_water. <5> 46 | 47 | ?- 48 | ---- 49 | 50 | <1> use_module CHR 51 | <2> define three _chr_constraints_, objects to put in the constraint store. 52 | <3> define a CHR rule - if salt and water are in the store, remove them and put in salt_water. 53 | <4> query that adds salt and water to the store. 54 | <5> CHR prints the contents of the store at the top level by default. The store contains salt_water. 55 | 56 | 57 | The Constraint Store 58 | -------------------- 59 | 60 | As we have seen, the constraint store holds state. We can put things, called _constraints_, in, and take them out. 61 | 62 | The constraint store can hold multiple copies of a constraint. It works like a multiset or bag. 63 | 64 | So, if you make salt water twice, you have two salt_waters. 65 | 66 | [NOTE] 67 | .Exercise - Multiset Semantics 68 | ===================================================================== 69 | Use the above program. 70 | 71 | What do you think will happen if you query this? Try it 72 | 73 | Query 74 | 75 | ---- 76 | ?- salt, water, salt. 77 | ---- 78 | 79 | How about this? 80 | 81 | Query 82 | 83 | ---- 84 | ?- salt, salt, water, water. 85 | ---- 86 | 87 | ===================================================================== 88 | Arguments 89 | ~~~~~~~~~ 90 | 91 | CHR constraints can have arguments of any Prolog type. 92 | Suppose we have a large number of beakers, and want to make 93 | salt water in them. We can add an argument to record which beaker we 94 | put the salt or water in. 95 | 96 | ---- 97 | :- use_module(library(chr)). 98 | 99 | :- chr_constraint salt/1, water/1, salt_water/1. <1> 100 | 101 | salt(N),water(N) <=> salt_water(N). <2> 102 | 103 | ?- salt(3), water(3). <3> 104 | salt_water(3). <4> 105 | 106 | ?- 107 | ---- 108 | 109 | <1> This time the chr_constraints have a single argument. salt(3) means there is salt in beaker 3. Like in Prolog, CHR constraints are defined by their **signature**. 110 | <2> define a CHR rule - if salt and water are in the same beaker, remove them and put in salt_water. 111 | <3> query that adds salt and water to beaker 3. 112 | <4> CHR prints the contents of the store at the top level by default. The store contains salt_water(3). 113 | 114 | When we call a CHR constraint from prolog, that argument unifies with the same position in the CHR constraint. However, the matching is not done in Prolog- it's done in CHR. And to CHR `foo(X)`, with X unbound, is a **different constraint** than `foo(2)`. 115 | 116 | [NOTE] 117 | .Exercise - arguments 118 | ===================================================================== 119 | Use the multi-beaker version of the program 120 | What do you think will happen if you query this? Try it 121 | 122 | Query 123 | 124 | ---- 125 | ?- salt(1),water(2), water(1). 126 | ---- 127 | 128 | How about this? 129 | 130 | Query 131 | 132 | ---- 133 | ?- salt(1),water(2), water(_). 134 | ---- 135 | 136 | And this? 137 | Query 138 | 139 | ---- 140 | ?- salt(1),water(2), water(X),X=3. 141 | ---- 142 | 143 | ===================================================================== 144 | Defining chr_constraints 145 | ~~~~~~~~~~~~~~~~~~~~~~~~ 146 | 147 | CHR constraints are defined using the `chr_constraint/1` directive, giving the constraint's signature. 148 | 149 | ---- 150 | :- chr_constraint foo/1. 151 | ---- 152 | 153 | This must be done **before the first mention in either prolog or CHR code**. 154 | 155 | CHR constraints can also be given types and modes, but we'll cover those later. 156 | 157 | What makes a good constraint? 158 | 159 | **Nouns** make good constraints. `salt` is really 'salt is present'. We could expand our salt water demo considerably, and end up with a chemist's advisor. 160 | 161 | A common pattern is to name all the relevant properties of an object. So we might not only say there's `salt`, but `ionic_compound` in some more chemistry oriented version of our salt water program. 162 | 163 | **States** make good constraints. In an interactive fiction type game of the `nanisearch` type, `player(bedroom)` is the state of the player being in the bedroom. 164 | 165 | States can be properties. `coughing`, meaning the patient is coughing, is a good CHR constraint. 166 | 167 | **Verbs** make good constraints. Verbs really say `thing_is_happening`. Usually, adding the verb to the constraint store makes things happen, and then the verb is removed at the end. 168 | 169 | Our salt water example has the defect that if we try it in the kitchen, we quickly discover adding salt is not enough. We need to stir as well, particularly if we've added a large quantity. 170 | 171 | Let's modify our program to require stirring. 172 | 173 | ---- 174 | :- use_module(library(chr)). 175 | 176 | :- chr_constraint salt/0, water/0, salt_water/0, stir/0. 177 | 178 | salt,water, stir <=> salt_water. 179 | 180 | ?- salt, water, stir. 181 | salt_water. 182 | 183 | ?- 184 | ---- 185 | 186 | But what happens if we use twice as much salt and water? 187 | 188 | ---- 189 | ?- salt, water, salt, water, stir. 190 | salt_water, 191 | salt, 192 | water. 193 | 194 | ---- 195 | 196 | We need two stirs. Let's assume this is not what we want and fix it. 197 | 198 | We made `salt_water`, let's also make `stir` on the right 199 | 200 | ---- 201 | :- use_module(library(chr)). 202 | 203 | :- chr_constraint salt/0, water/0, salt_water/0, stir/0. 204 | 205 | salt,water, stir <=> salt_water, stir. 206 | 207 | ?- salt, water, salt, water, stir. 208 | salt_water, 209 | salt_water, 210 | stir. 211 | 212 | ?- 213 | ---- 214 | 215 | Oh fudge - `stir` remains. 216 | 217 | We can fix this with a second rule. This rule will only fire after the first rule can't fire any more. More on this later, but for now, the topmost rule that can fire, does. 218 | 219 | ---- 220 | :- use_module(library(chr)). 221 | 222 | :- chr_constraint salt/0, water/0, salt_water/0, stir/0. 223 | 224 | salt,water, stir <=> salt_water, stir. 225 | stir <=> true. 226 | 227 | ?- salt, water, salt, water, stir. 228 | salt_water, 229 | salt_water. 230 | 231 | ?- 232 | ---- 233 | 234 | We keep making salt water until we can't, and then we stop stirring. 235 | 236 | [NOTE] 237 | .Exercise - making constraints 238 | ===================================================================== 239 | Try all the above examples in this section. 240 | 241 | If we leave the salt water for `time`, it will evaporate back to `salt`. 242 | Add a new CHR constraint `time/0` and when you have `salt_water` and `time` 243 | return to `salt`. When you have `water` and time, go back to nothing (put true 244 | on the right). 245 | ===================================================================== 246 | 247 | Basic CHR syntax 248 | ---------------- 249 | 250 | It's time to introduce the CHR syntax in more detail. 251 | There are a few bits 252 | of syntax which will be omitted here and covered later. 253 | 254 | We have seen one operator, `<=>`, informally. There are actually 3 operators. 255 | 256 | ---- 257 | name @ discarded <=> guard | body. <1> Simplification 258 | name @ retained \ discarded <=> guard | body. <2> Simpagation 259 | name @ retained ==> guard | body. <3> Propagation 260 | ---- 261 | 262 | All rules have the same structure regardless of their operator. 263 | 264 | Name 265 | ~~~~ 266 | 267 | First comes an **optional** _name_, a unique identifier for the rule. This is used by various CHR tools, 268 | but has no effect on the _operation_ of CHR. _name_ is followed by an `@` separator if included. 269 | We will not discuss names further. 270 | 271 | Head 272 | ~~~~ 273 | 274 | Next comes the _head_. The head is a series of CHR constraints which must **all** be present for the rule to fire. 275 | In our first salt water example, both salt and water must be present. 276 | 277 | As we've already seen, we can store more than one `salt` in the store. We can have two `salt`s in the head, in which case we two or more salts in the store to fire the rule. 278 | 279 | [NOTE] 280 | .Exercise - set semantics 281 | ===================================================================== 282 | Let's see if we can change the first program, so `salt` means any non-zero amount of salt, 283 | and `water` means any non-zero 284 | amount of water, and we only get one `salt_water`. 285 | 286 | Hint: you want two salts to become one salt. 287 | ===================================================================== 288 | 289 | This **set semantics** is a frequently useful pattern. 290 | 291 | 292 | Matching 293 | ^^^^^^^^ 294 | 295 | So what does it mean to match? 296 | 297 | Generally this is straightforward - it unifies like Prolog. 298 | 299 | We saw an example of using this earlier, in the multiple beaker 300 | version of salt water. 301 | 302 | ---- 303 | salt(N),water(N) <=> salt_water(N). 304 | ---- 305 | 306 | If there's a `salt(3)` and a `water(4)` then we don't get salt water. The arguments must match. 307 | 308 | Now we come to a gotcha. 309 | If you add a constraint from Prolog, be cognizant of **what constraint you are actually adding**. 310 | 311 | Suppose you have a constraint `foo/1`, and want to get the values. 312 | 313 | ---- 314 | % doesn't work 315 | get_foo_back(X) :- 316 | foo(X). 317 | ---- 318 | 319 | This doesn't work because it instead **adds foo with an unbound variable to the store!** 320 | 321 | So let's try adding a CHR rule 322 | 323 | ---- 324 | % still doesn't work 325 | get_foo_back(X) :- 326 | get_foo(X). 327 | 328 | foo(A), get_foo(A) ==> true. 329 | ---- 330 | 331 | Assume we have `foo(3)` in the store. 332 | This still doesn't work because CHR does: 333 | 334 | . Prolog adds `get_foo(X)` with X unbound 335 | . CHR looks for a rule to fire. It finds `foo(3)` in the store and grounds A to 3. 336 | . CHR then looks for `get_foo(3)` (since we've bound the variable), and, while there's a `get_foo` with an **unbound variable**, there isn't one with a 3. Oops. 337 | 338 | Getting information back from the store to Prolog is covered in <>. 339 | 340 | Operator 341 | ~~~~~~~~ 342 | 343 | There are 3 operators in CHR. 344 | 345 | We have seen the first type of rule, **Simplification**, denoted by the operator `<=>`. 346 | What is on the left hand side is **discarded** from the store, and then the right hand side(RHS) is done. 347 | 348 | So far rules have always removed their head constraints. If we don't want this, we have to use a workaround, re-adding the constraint. 349 | 350 | The **Simpagation** rule type eliminates the remove/re-add workaround. It uses the same operator `<=>` as the **Simplification** operator, but has a 351 | `\` backslash in the head. The constraints to the left of the `\` are retained in the store, and those to the right removed. 352 | 353 | Let's improve our single stir version. 354 | 355 | ---- 356 | :- use_module(library(chr)). 357 | 358 | :- chr_constraint salt/0, water/0, salt_water/0, stir/0. 359 | 360 | stir \ salt, water <=> salt_water. 361 | stir <=> true. 362 | 363 | ?- salt, water, salt, water, stir. 364 | salt_water, 365 | salt_water. 366 | 367 | ?- 368 | ---- 369 | 370 | The third rule type, **Propagation**, retains *all* of the constraints in the head. 371 | 372 | Why not just have one rule type, and re-add the ones you want to retain in the body? What's wrong with 373 | the workaround? 374 | 375 | Because when you remove and re-add the constraint, it **isn't the same constraint**. So 376 | rules _activate_ again. 377 | 378 | Sally works in a garage. She's been instructed to fill out a new work form every time she works on a car. 379 | Bob has brought his car into the garage to have the oil changed. Sally follows the rule, and fills in 380 | a work form for Bob's car. 381 | 382 | Here's a snippet of CHR that implements the work form rule. If we remove and re-add the car, it keeps firing 383 | 384 | ---- 385 | % DOESN'T WORK - infinite loop 386 | car(Owner, Work) <=> car(Owner, Work), work_form(Owner, Work). 387 | 388 | % WORKS 389 | car(Owner, Work) ==> work_form(Owner, Work). 390 | ---- 391 | 392 | 393 | 394 | [NOTE] 395 | .Exercise - Propagation rule 396 | ===================================================================== 397 | Add _noise_ to the constraint store when stirring occurs. Add one noise 398 | for every stir added from outside. 399 | 400 | Test your work - does it work even if you don't make salt water? 401 | Do you make many noises if there's lots of salt water being made? 402 | ===================================================================== 403 | 404 | [NOTE] 405 | .Exercise - Garage 406 | ===================================================================== 407 | When the garage finishes the work, Prolog adds a constraint `work_done(Owner)`. 408 | 409 | Then the owner comes, pays the bill, and takes the car. Prolog adds `pays(Owner)`, 410 | then `pickup(Owner)`. 411 | 412 | Implement rules to model this process. Don't let the customer take the 413 | car without paying the bill. 414 | (looking ahead, you can make `pickup(Owner)` fail in Prolog by failing the body). 415 | 416 | Hint: Which controls the process? The **car**, or the **paperwork**? 417 | 418 | Hint: car/2 means something like 'There is a car at the shop. It is owned by `Owner` and 419 | was brought in to have `Work` done. 420 | 421 | ===================================================================== 422 | 423 | [NOTE] 424 | .Exercise - Garage Additional Work 425 | ===================================================================== 426 | Extra credit- harder 427 | 428 | Often when a customer takes their car to the garage, the garage 429 | finds it needs to change what work is done. 430 | 431 | Add rules to deal with this. Beyond that, use this exercise to build your skills, 432 | dealing with things like cars we don't end up working on ("Sorry Ms. Smith, 433 | your car needs a new engine, and it would cost more than the car is worth."). 434 | 435 | Hint: The car needing more work is a change to the **paperwork**, not the car. 436 | 437 | ===================================================================== 438 | 439 | Guard 440 | ~~~~~ 441 | 442 | Until now we've just been putting a CHR constraint on the right hand side. On one occasion 443 | we used `true`. But the right hand side is much more flexible. 444 | 445 | We haven't used a guard yet. A _guard_ is an optional prolog query. If it succeeds, and if the appropriate 446 | constraints are in the store to match the left hand side, then, and only then, will the rule fire. 447 | 448 | Suppose we have a bunch of constraints like `quake(Intensity)`. Our instrument is quite sensitive, and we 449 | have many small tremors we can discard. Let's discard all quakes less than 3.0 . 450 | 451 | --------------------------------------------- 452 | quake(Intensity) <=> Intensity < 3.0 | true. 453 | 454 | --------------------------------------------- 455 | 456 | You can have multiple goals in the guard. They execute left to right in the usual Prolog manner. 457 | There is no backtracking (you can't bind anything anyway). They can share variables with the head, 458 | (as long as they don't bind them) and with each other. 459 | 460 | Try to minimize use of guards for **performance**. Every time the rule might fire, the guard must be executed. 461 | 462 | Now, a warning. **You may NOT bind variables in the guard**. 463 | Why this restriction? The guard will be called whenever Prolog decides to check if 464 | the guard might succeed. 465 | If the guard binds variables, simply checking a variable can ground it, or another variable. 466 | 467 | [WARN] 468 | .Don't Bind Variables in Guard 469 | ===================================================================== 470 | The behavior of binding variables in the guard is undefined, and the 471 | CHR compiler will flag it. 472 | ===================================================================== 473 | 474 | 475 | [NOTE] 476 | .Constraints 477 | ================================================================= 478 | This material requires understanding constraint programming. 479 | If you don't, you can skip over it and know you just shouldn't 480 | bind variables in the guard. 481 | Alternatively, check out 482 | link:http://www.pathwayslms.com/swipltuts/clpfd/clpfd.html[my clp(fd) tutorial] 483 | ================================================================= 484 | 485 | Guards have one other very useful property - **Reactivation**. We might have a guard whose success can change 486 | **without adding a constraint to the store** 487 | 488 | ---- 489 | more_than_3(N) <=> ground(N) | N > 3. 490 | 491 | ?-more_than_3(X),writeln('middle'),X = 2. 492 | middle 493 | false. 494 | ---- 495 | 496 | This guard can change if we add `more_than_3(X)` and then later **ground X**. 497 | 498 | When CHR encounters such a guard, the rule does not fire, but a **unification hook** is attached to the variable. When the variable later grounds, the rule fires. This may cause **prolog** to fail at this point. 499 | 500 | This allows us to build **constraint systems**. 501 | 502 | Note that the above code says 'middle', and only fails when X is bound. 503 | 504 | Since we can later change the constraint we can develop efficient constraint checkers. 505 | If we apply `more_than_4` to X then we _subsume_ `more_than_3`. We can discard the weaker constraint. 506 | 507 | ---- 508 | more_than_4(X) \ more_than_3(X) <=> true. 509 | ---- 510 | 511 | [Exercise] 512 | .Exercise - Intervals 513 | ===================================================================== 514 | Define a constraint `interval/2` whose left argument is the lower end 515 | and whose right argument is the upper end. 516 | 517 | Define rules to simplify the constraint store. 518 | 519 | For example, interval(1, 5) and interval(4, 9) can become interval(1, 9). 520 | Don't forget nesting, like interval(1, 5), interval(2,3). 521 | 522 | Stretch goal: don't force the user to enter the intervals (low,high). 523 | If they come in (high, low), swap them around. 524 | 525 | This exercise is a bit more complex than what you've been doing. 526 | ===================================================================== 527 | 528 | Body 529 | ~~~~ 530 | 531 | The final part of a CHR rule is the body. This is NOT a prolog rule body. It *is* a comma separated series of **Prolog rules** and **CHR Constraints**. CHR constraints are added to the constraint store as if called from Prolog. 532 | If a Prolog goal or chr_constraint fails **the entire attempt to add the original constraint fails**, and the store is returned to it's original state prior to the attempt. 533 | 534 | You **ARE** allowed to bind arguments in the body. This becomes important when we look at getting data back from the store. 535 | 536 | Which Rule Fires? 537 | ----------------- 538 | 539 | When a constraint is added to the store, CHR adds the constraint, then **looks downward from the top of the store until it finds a rule that can fire**. It fires the rule. It then returns to the top and tries to find a rule to fire again. There is no backtracking. 540 | When there are no more rules that can fire, the loop returns to the caller. 541 | 542 | If the rule adds new constraints on the RHS, then the entire algorithm applies recursively at that point. 543 | So 544 | 545 | ---- 546 | foo ==> bar,baz. 547 | 548 | ---- 549 | 550 | We add foo. We find the above rule and add **bar only**. We added a constraint, so we **look for a rule match**. 551 | Now we add baz. We added a constraint, so we **look for a rule match**. 552 | 553 | When CHR is 'just sitting there' no constraint is active. When we call a chr_constraint from Prolog, it 554 | is added and made the _active_ constraint. If the rule causes other rules to be added, in turn they will 555 | be the _active_ constraint. Only rules that contain the active constraint are checked. 556 | 557 | This makes the store more _stable_. You needn't worry about some unrelated action firing a rule. 558 | 559 | 560 | [Exercise] 561 | .Exercise - Does this terminate? 562 | ===================================================================== 563 | 564 | ---- 565 | :- chr_constraint moo/0,bar/0. 566 | 567 | moo ==> bar. 568 | 569 | ?- moo. 570 | ---- 571 | 572 | Think about this. If I add moo then I make bar, then look for another 573 | rule, find the same one, and _keep going_? No? (think about it, then 574 | keep reading). 575 | ===================================================================== 576 | 577 | It terminates. 578 | 579 | rules **do not re-fire** if both of: 580 | 581 | * The exact same constraints (not just type) are involved. Thus the `salt,salt,water,water` example works. 582 | And this answers our 'keep going' question. 583 | * Prolog in the body didn't leave choice points. 584 | 585 | [Exercise] 586 | .Exercise - How many solutions do I get? 587 | ===================================================================== 588 | :- chr_constraint backtrack/1. 589 | 590 | backtrack(X) ==> member(X, [a,b,c]). 591 | 592 | ?- backtrack(X). 593 | ===================================================================== 594 | 595 | What happens if the Prolog **fails**? 596 | 597 | If that fails, the **entire attempt** to add the constraint to the store will fail, and the CHR store will roll back to it's previous state. The original Prolog that added the constraint will **fail**. 598 | 599 | Notice that what it will NOT do is try other valures or go on to the next rule. Failure stops the entire show. 600 | 601 | [Exercise] 602 | .Exercise - Demonstrate failure 603 | ===================================================================== 604 | Fiddle about and demonstrate failure. 605 | what happens if you add a constraint to the store and then have the original calling prolog fail? 606 | 607 | For that matter, what happens if you throw? 608 | 609 | Extra credit: what happens if you throw a delimited continuation and 610 | it resets? 611 | ===================================================================== 612 | 613 | What happens if the Prolog in the body leaves **choice points** that aren't consumed later in the body? 614 | Then if the **original calling prolog** 'sees' a choice point. 615 | 616 | Recursion is often a useful pattern. 617 | 618 | [Exercise] 619 | .Exercise - 8888 620 | ===================================================================== 621 | Let's get lucky. Many Chinese folks think numbers like 8888 are auspicious. 622 | 623 | Make a constraint `lucky/1`. Only add to the store those that are 'lucky' (a series 624 | of 8's). 625 | 626 | ---- 627 | lucky(888). % stays in store 628 | lucky(888888888). % stays in store 629 | lucky(77). % remove 630 | lucky(8). % stays in store 631 | ---- 632 | 633 | Hint: you will need **two** constraints, one to stay in the store and one to add 634 | to the store 635 | ===================================================================== 636 | 637 | A bit of larger advice. When recognizing, there is an inherent conflict between adding 638 | things to the store and replacing them. 639 | Suppose we have a recognizer for drawings. It's handed this drawing: 640 | 641 | image:house.png[childs drawing of house] 642 | 643 | We define a house as a triangle atop a box. Well, if we find the 'triangle' part first, 644 | and turn it into a triangle, we're left with 645 | 646 | image:housebody.png["3 lines, 2 vertical and one at bottom"] 647 | 648 | Which isn't a 'box' any more. In this case it's probably better to leave 'lines' in the store. 649 | 650 | On the other hand, if you recognize a squiggle as a 'line' (as opposed to an S shape or a circle), it's 651 | probably better to remove the squiggle and keep the line. 652 | 653 | Making CHR interact with Prolog 654 | ------------------------------- 655 | 656 | As we've seen, calling a CHR constraint from Prolog causes it to be added to the store. 657 | 658 | If Prolog in the **body** of any rule fails, all changes to the store since the 'original' 659 | attempt to add a constraint (by calling it from Prolog) are **rolled back**. 660 | The Prolog itself then fails to that point. 661 | 662 | ---- 663 | 664 | :- chr_constraint blah/0. 665 | 666 | some_prolog :- 667 | writeln('got here'), 668 | blah, 669 | writeln('Prolog will fail before it gets here'). 670 | 671 | blah ==> fail. 672 | 673 | ?- some_prolog. 674 | got here 675 | false. 676 | ---- 677 | 678 | If the constraint rules call Prolog and generates choice points, the constraint succeeds with choice points. 679 | 680 | If the constraint encounters a guard that might change when a variable is grounded, the constraint is subject to **reactivation**. See the section above on [[Guards]] 681 | 682 | Threads 683 | ~~~~~~~ 684 | 685 | A CHR store is **local to one thread**. 686 | 687 | This is particularly painful when implementing a server that uses CHR. 688 | 689 | One solution is to do all the CHR work on a special thread. 690 | 691 | Falco Nogatz's 692 | link:https://github.com/fnogatz/CHR-Constraint-Server[CHR-Constraint-Server] is a useful tool. 693 | 694 | The 695 | link:https://github.com/SWI-PrologTeamLudumDare32/LudumDare45['3 Little Pigs' game] 696 | is a useful starter for a server that uses CHR for it's logic. 697 | 698 | A Pengine will have it's own thread. This can be useful for CHR. 699 | 700 | [[Getting]] 701 | Getting 702 | ------- 703 | 704 | What hasn't been mentioned to now is how to get a value back to prolog. 705 | 706 | If we only need a generator, to get the solutions on backtracking, this is relatively straightforward. 707 | Make a get_thing constraint that grabs the thing on the RHS. 708 | 709 | ---- 710 | :- chr_constraint thing/1, get_thing/1. 711 | 712 | thing(N) \ get_thing(M) <=> N = M. 713 | ---- 714 | 715 | Notice that I'm binding N to M **on the right hand side**. 716 | 717 | This makes the constraints matched in the head `thing` with **any argument**, and `get_thing` with **any argument**. 718 | They need not be the same at this point. 719 | On the RHS we're going to hope `get_thing`'s argument is unground, and unify it with `thing`'s argument. 720 | 721 | If there are many things, and we don't mind backtracking to get them, this works as well. 722 | EG if you just want to print them out, a repeat-fail list works 723 | 724 | ---- 725 | print_things :- 726 | repeat, 727 | get_thing(N), 728 | writeln(N), 729 | fail. 730 | print_things. 731 | ---- 732 | 733 | But what if there are many things, and we want to consolidate them in a list? 734 | Unfortunately `findall/3`, `setof/3`, bagof/3` etc. don't work. 735 | 736 | We use a pattern which I've named the _get_foo_ pattern. 737 | 738 | ---- 739 | :- use_module(library(chr)). 740 | 741 | :- chr_constraint foo/1,one_foo/1, collect_foo/1, get_foo/1. <1> 742 | 743 | load_it :- <2> 744 | foo(1), 745 | foo(3), 746 | foo(7). 747 | 748 | % copy constraints to be collected 749 | foo(X), get_foo(_) ==> one_foo(X). <3> 750 | get_foo(L) <=> collect_foo(L). <4> 751 | 752 | % collect and remove copied constraints 753 | one_foo(X), collect_foo(L) <=> <5> 754 | L=[X|L1], collect_foo(L1). 755 | collect_foo(L) <=> L=[]. <6> 756 | 757 | go(X) :- load_it, get_foo(X). <7> 758 | ---- 759 | 760 | <1> There are four moving pieces. 761 | * `foo/1`, the constraint. 762 | * `one_foo/1`, a temporary copy of the constraint 763 | * `collect_foo/1`, a place to hold the list built up so far 764 | * `get_foo/1` the API to the outside Prolog 765 | <2> Let's load some constraints 766 | <3> We'll copy all the constraints, then delete the copies as we collect them into the list 767 | <4> When we start we swap `get_foo` for `collect_foo`, passing along the argument. 768 | <5> we discard a `one_foo`, and the current `collect_foo`. We bind `collect_foo`'s argument **L** 769 | to a **hole list**, a list whose final element is the unbound L1. We then add a new `collect_foo` with L1 to fill in the rest of the list. 770 | <6> Eventually we run out of `one_foo`'s. We transform the **hole list** to a normal list by 'plugging the hole' with an empty list. 771 | <7> convenience predicate to run the system. 772 | 773 | Helpful Utilities 774 | ----------------- 775 | 776 | There is a debugger for CHR similar to the old four port debugger. `chr_trace/0` and 777 | `chr_notrace/0` invoke it. 778 | 779 | ---- 780 | % put this right at beginning for production 781 | % disable tracing (makes it run faster by removing debug ) 782 | :- chr_option(debug, off). 783 | % let it optimize 784 | :- chr_option(optimize, experimental). 785 | 786 | %slower execution, easier understanding of debugger output 787 | :- chr_option(debug, on). :- chr_option(optimize, off). 788 | ---- 789 | 790 | CHR clears the constraint store when it returns to the top level. 791 | This can be annoying when learning or debugging if you want to manually 792 | manipulate the store. 793 | 794 | Here are a couple ways to avoid this. The first repeatedly breaks into 795 | a new level of the interactor. The second is to use a flag that does effectively 796 | the same thing, but you don't have to remember to do it each time. 797 | 798 | ---- 799 | % avoid the constraint store evaporating at top level. 800 | ?- run_my_program, break. 801 | 802 | % saner way to do same 803 | ?- set_prolog_flag(toplevel_mode, recursive). 804 | ---- 805 | 806 | `current_chr_constraint/1` is intended for debugging, and is slow. 807 | Don't use this as a pattern for reading the constraint store. 808 | but I often add this to my CHR programs as a handy debug tool. 809 | 810 | ---- 811 | % print out the constraint store 812 | ps :- 813 | current_chr_constraint(M:Y), 814 | format('constraint store contains ~w:~w~n', [M,Y]), 815 | fail. 816 | ps. 817 | ---- 818 | 819 | CHR prints out the contents of the constraint store when you return to the top 820 | level. This behavior can be helpful or annoying dependin on the situation. 821 | 822 | Here's some debug code to turn it on and off. 823 | 824 | ---- 825 | % print out constraint store when you return to top level 826 | ss :- set_prolog_flag(chr_toplevel_show_store, true). 827 | 828 | % or don't 829 | noss :- set_prolog_flag(chr_toplevel_show_store, false). 830 | ---- 831 | 832 | Conclusion 833 | ---------- 834 | 835 | This completes the basics of CHR. You should be ready to study 836 | link:constraintsystems.html[Constraint Systems] 837 | 838 | This is also a great time to work some of the 839 | link:examples.html[Examples and Patterns] 840 | 841 | Useful references 842 | 843 | * https://dtai.cs.kuleuven.be/CHR/files/tutorial_iclp2008.pdf 844 | * https://dtai.cs.kuleuven.be/CHR/ 845 | * https://dtai.cs.kuleuven.be/CHR/webchr.shtml 846 | * https://www.swi-prolog.org/pldoc/man?section=chr 847 | 848 | -------------------------------------------------------------------------------- /cellautomata.pl: -------------------------------------------------------------------------------- 1 | 2 | % http://mathworld.wolfram.com/CellularAutomaton.html 3 | % 4 | :- use_module(library(chr)). 5 | 6 | :- chr_constraint cell_rule/4, cell/5, init_cells/0, init_a_cell/1. 7 | 8 | init :- 9 | % L C R N 10 | cell_rule(w, w, w, w), 11 | cell_rule(w, w, b, b), 12 | cell_rule(w, b, w, b), 13 | cell_rule(w, b, b, b), 14 | cell_rule(b, w, w, b), 15 | cell_rule(b, w, b, w), 16 | cell_rule(b, b, w, w), 17 | cell_rule(b, b, b, w), 18 | init_cells. 19 | 20 | % cells are indexed 0 to 19 21 | % cells 10 starts black, others start white 22 | 23 | % propagate new generation 24 | % we're in the middle and can figure out color from 25 | % rules 26 | cell(Pos, R, _, N, LClr), 27 | cell(_, Pos, _, N, PosClr), 28 | cell(_, L, Pos, N, RClr), 29 | cell_rule(LClr, PosClr, RClr, NewClr) 30 | ==> 31 | N < 10 | % stop after 10 generations 32 | succ(N, NN), 33 | cell(L, Pos, R, NN, NewClr). 34 | cell(_, 0, _, N, _) ==> 35 | N < 10 | 36 | succ(N, NN), 37 | cell(_, 0, _, NN, w). 38 | cell(_, 19, _, N, _) ==> 39 | N < 10 | 40 | succ(N, NN), 41 | cell(_, 19, _, NN, w). 42 | 43 | init_cells <=> init_a_cell(19). 44 | 45 | init_a_cell(0) <=> 46 | cell(none, 0, 1, 1, w). 47 | init_a_cell(19) <=> 48 | cell(18, 19, none, 1, w), 49 | init_a_cell(18). 50 | init_a_cell(N) <=> 51 | succ(L, N), 52 | succ(N, R), 53 | cell(L, N, R, 1, w), 54 | init_a_cell(L). 55 | 56 | 57 | print_cells :- 58 | between(0, 19, Row), 59 | get_line(Row, Line), 60 | format('~s~n', [Line]), 61 | fail. 62 | print_cells. 63 | 64 | :- chr_constraint get_line/2, temp_cell/3, collect_line/3. 65 | 66 | get_line(Row, _), cell(_, Row, _, N, Val) ==> 67 | temp_cell(Row, N, Val). 68 | get_line(Row, Line) <=> collect_line(Row, 1, Line). 69 | 70 | collect_line(_, Gen, Out) <=> Gen >= 10 | Out = []. 71 | collect_line(Row, Gen, Out) \ 72 | temp_cell(Row, Gen, Val) <=> 73 | succ(Gen, NG), 74 | convert_val(Val, OutVal), 75 | Out = [OutVal | Rest], 76 | collect_line(Row, NG, Rest). 77 | 78 | convert_val(b, 0'b). 79 | convert_val(w, 0'w). 80 | convert_val(none, 0'!). 81 | convert_val(_, 0'?). 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /chr_experiments.pl: -------------------------------------------------------------------------------- 1 | /* 2 | * CHR syntax, since I can never remember it: 3 | * name @ retained \ discarded <=> guard | head,body. Simpagation 4 | * name @ discarded <=> guard | head, body. Simplification 5 | * name @ retained ==> guard | head, body. Propagation 6 | * 7 | */ 8 | :- use_module(library(chr)). 9 | 10 | 11 | :- chr_constraint 12 | foo/1, 13 | bar/1, 14 | mep/1, 15 | zap/1. 16 | 17 | foo(X) ==> member(X, [a,b,c]), bar(X). 18 | 19 | % doesn't work 20 | % mep(L) ==> member(X, L), zap(X). 21 | 22 | mep(L) ==> foreach(member(X, L), zap(X)). 23 | 24 | % how do I negate? 25 | :- chr_constraint nord/0, noodge/0, dingle/0. 26 | 27 | nord ==> \+ find_chr_constraint(noodge) | dingle. 28 | 29 | % better 30 | nord, noodge ==> true. 31 | nort ==> dingle. 32 | 33 | % Examples to write 34 | % 35 | % cellular automaton from schrijvers slides slide 82 36 | % 37 | %domain constraint from slides 96 38 | % 39 | % 40 | % toy project - recipe reasoner 41 | % (use drinks) 42 | 43 | % toy project - radioactive decay 44 | % given half lives, put in atoms and watch'em decay 45 | % keeping track of time 46 | 47 | % adventure game. 48 | 49 | % 'properly dressed' puzzle 50 | 51 | %patterns 52 | % domain constraint from slides 96 - pattern, guard for instantiation 53 | % to implement constraint solver 54 | % 55 | 56 | % backtracking undoes changes to constraint store 57 | % 58 | 59 | % pattern - backtracking for labeling 60 | % 61 | 62 | % pattern - get_gcd is a chr_constraint used only 63 | % to get the value of another constraint. 64 | % Not sure if this backtracks 65 | % it does NOT, apparently 66 | % 67 | % Note that the binding happens in the BODY, cause binding in the 68 | % head is a nono 69 | % 70 | % constraint to get the current gcd/1 value 71 | % gcd(N) \ get_gcd(M) <=> M = N. 72 | % 73 | % that won't backtrack, this will 74 | % gcd(N), get_gcd(M) ==> M = N. 75 | % get_gcd(_) <=> true. 76 | % 77 | % that doesn't backtrack either 78 | 79 | % This is the canonical get_ pattern per Thom Fruewirth 80 | 81 | % no backtrack get 82 | foo(X) \ get_foo(Y) <=> 83 | X = Y. 84 | get_foo(_) <=> fail. 85 | 86 | % Yay, backtracking get that ACTUALLY WORKS 87 | 88 | % ========== entire file ========= 89 | :- use_module(library(chr)). 90 | 91 | :- chr_constraint foo/1,one_foo/1, collect_foo/1, get_foo/1. 92 | 93 | load_it :- 94 | foo(1), 95 | foo(3), 96 | foo(7). 97 | 98 | % copy constraints to be collected 99 | foo(X), get_foo(_) ==> one_foo(X). 100 | get_foo(L) <=> collect_foo(L). 101 | 102 | % collect and remove copied constraints 103 | one_foo(X), collect_foo(L) <=> 104 | L=[X|L1], collect_foo(L1). 105 | collect_foo(L) <=> L=[]. 106 | 107 | go(X) :- load_it, get_foo(X). 108 | 109 | ================= 110 | 111 | 112 | /* 113 | :- use_module(library(chr)). 114 | 115 | :- chr_constraint foo/1, get_foo/1. 116 | 117 | load_it :- 118 | foo(1), 119 | foo(3), 120 | foo(7). 121 | 122 | foo(X), get_foo(Y) ==> 123 | X = Y. 124 | get_foo(_) <=> true. 125 | 126 | % library(chr) compiled into chr 0.52 sec, 133 clauses 127 | % /home/anniepoo/prologhelp/chrtest compiled 0.53 sec, 57 clauses 128 | ?- load_it, find_all(X, get_foo(X), L). 129 | Correct to: "findall(X,get_foo(X),L)"? yes 130 | L = [], 131 | foo(7), 132 | foo(3), 133 | foo(1). 134 | 135 | ?- 136 | 137 | So, this is ugly as hell, but works 138 | 139 | all_foos(L) :- 140 | nb_setval(all_foos_val, []), 141 | af(L). 142 | 143 | af(_) :- 144 | find_chr_constraint(foo(X)), 145 | nb_getval(all_foos_val, OldL), 146 | nb_setval(all_foos_val, [X | OldL]), 147 | fail. 148 | af(L) :- 149 | nb_getval(all_foos_val, L). 150 | 151 | doesnt work 152 | 153 | foo(X) \ gather_foos_together(Old) <=> gather_foos_together([X | Old]). 154 | 155 | gather_foos_together(Foos), get_thy_foos(Y) ==> Foos = Y. 156 | % get_thy_foos(_) <=> true. don't do this 157 | 158 | */ 159 | 160 | 161 | /******************************* 162 | * helpful utilities * 163 | *******************************/ 164 | 165 | % print out the constraint store 166 | % 167 | ps :- 168 | find_chr_constraint(Y), 169 | format('constraint store contains ~w~n', [Y]), 170 | fail. 171 | ps. 172 | 173 | % print out constraint store when you return to top level 174 | ss :- set_prolog_flag(chr_toplevel_show_store, true). 175 | 176 | % or don't 177 | noss :- set_prolog_flag(chr_toplevel_show_store, false). 178 | 179 | 180 | 181 | % 182 | % name @ retained \ discarded <=> guard | head,body. Simpagation 183 | % discarded <=> guard | head, body. Simplification 184 | % retained ==> guard | head, body Propagation 185 | 186 | mumble, mumble, mumble <=> writeln('3 mumbles') | true. 187 | mumble ==> mumble. 188 | 189 | doorbell :- 190 | mumble. 191 | 192 | 193 | 194 | fc(X) :- 195 | functor(X, XF, N), 196 | functor(Y, XF, N), 197 | find_chr_constraint(Y), 198 | subsumer(X, Y). 199 | 200 | subsumer(A, B) :- 201 | copy_term(B, BCopy) 202 | , catch(A = B, _, fail) 203 | , =@=(B, BCopy) 204 | . 205 | 206 | /* 207 | ?- fc(my_con(A, 3, B)), 208 | fc(my_con(B, 3, C)). 209 | 210 | % above is O(n^2) 211 | :- chr_constraint my_con/3. 212 | 213 | % above is linear 214 | :- chr_constraint my_con(+dense_int, +dense_int, +dense_int). 215 | 216 | % my_con(A,3,B), my_con(B, 3, C) ==> something. 217 | 218 | */ 219 | 220 | /* 221 | * 222 | * 223 | 224 | fc(X) :- 225 | functor(X, XF, N), 226 | functor(Y, XF, N), 227 | find_chr_constraint(Y), 228 | subsumer(X, Y). 229 | 230 | subsumer(A, B) :- 231 | copy_term(B, BCopy) 232 | , catch(A = B, _, fail) 233 | , =@=(B, BCopy) 234 | . 235 | 236 | 237 | ?- fc(my_con(A, 3, B)), 238 | fc(my_con(B, 3, C)). 239 | 240 | 241 | :- chr_constraint my_con(+dense_int, +dense_int, +dense_int). 242 | 243 | my_con(A,3,B), my_con(B, 3, C) ==> something. 244 | 245 | % make all connected edges 246 | a-b b-c c-d 247 | a-d 248 | :- chr_constraint node/2. 249 | node(A,B) \ node(A,B) <=> true. % anti-cycle 250 | 251 | node(A,B), node(B,C) ==> node(A,C). 252 | 253 | ?- node(1,2), node(2,3), node(3,1). 254 | 255 | query(5, 2, X). 256 | :- chr_constraint query(+, +, -). 257 | node(A,D), node(B,E) 258 | \ query(A,B,C) 259 | <=> C is E + D. 260 | 261 | % print out execution without pausing 262 | ?- chr_leash(-all). chr_trace. query(2,3,N). 263 | 264 | % better to have init ==> task(customize),task(help),security(csrf_in_forms)... 265 | % even better , since we don't want init 266 | init <=> task(customize),task(help),security(csrf_in_forms)... 267 | % but even better, just do it in prolog 268 | init :- task(customize),task(help),security(csrf_in_forms). 269 | % instead of 270 | init ==> security(remove_test_reset). 271 | init ==> task(implement_localization_hook). % customize 272 | init ==> task(set_setting(identity:style)). % customize 273 | init ==> task(attach_database). 274 | 275 | ?- listing(init). 276 | 277 | a \ a <=> true. 278 | 279 | % keep all_email_tasks, and only add once using 280 | all_email_tasks \ all_email_tasks <=> 281 | 282 | :-chr_constraint all_email_tasks(+dense_int, +dense_int, +dense_int). 283 | 284 | % only do it once pattern 285 | all_email_tasks( RI, A, B) 286 | \ create_all_email(RI ,B) 287 | <=> true. 288 | 289 | create_all_email(RI ,B) 290 | <=> gen_sym(A), all_email_tasks( RI, A, B). 291 | 292 | ?- create_all_email(RI ,B), create_all_email(RI ,B). 293 | 294 | % this is Ok 295 | completed(decision(remember_me), X) ==> X \= none | all_remember_me_tasks(X). 296 | 297 | user(X, UserData) 298 | , completed(decision(remember_me), X) ==> X \= none | all_remember_me_tasks(X). 299 | 300 | % put this right at beginning for production 301 | % disable tracing (makes it run faster by removing debug ) 302 | :- chr_option(debug, off). 303 | % let it optimize 304 | :- chr_option(optimize, experimental). 305 | 306 | %slow, easy 307 | :- chr_option(debug, on). :- chr_option(optimize, off). 308 | 309 | % avoid the constraint store evaporating at top level. 310 | ?- run_my_program, break. 311 | 312 | % saner way to do same 313 | ?- set_prolog_flag(toplevel_mode, recursive). 314 | 315 | 316 | ttmrichter 317 | 318 | [21:35] I'm simulating a CPU core. 319 | [21:35] It has an 8-bit register, a couple of 16-bit registers, and a 24-bit register. 320 | [21:35] A simplistic "just store this value" is not a good thing. 321 | [21:35] accumulator @ a(A), ra(_) <=> u8(A) | ra(A). 322 | [21:35] a(A), ra(_) <=> \+u8(A) | fail. 323 | [21:35] So I do that. 324 | [21:36] a(+A) sets the accumulator value of register A iff the provided value fits in an unsigned integer width. 325 | [21:37] The whole .... guard | action. ... \+guard | fail. pattern is everywhere in my code. 326 | 327 | 328 | useful cheatsheet 329 | 330 | https://dtai.cs.kuleuven.be/CHR/files/CHR_cheatsheet.pdf 331 | 332 | Falco Nogatz worked on this 333 | http://www1.informatik.uni-wuerzburg.de/en/news/single/news/improving-deutsche-bahn-with-prolog/ 334 | 335 | https://github.com/fnogatz/CHR.js-website 336 | */ 337 | 338 | -------------------------------------------------------------------------------- /chrtest.pl: -------------------------------------------------------------------------------- 1 | :- module(chrtest, []). 2 | :- use_module(library(chr)). 3 | 4 | :- chr_constraint foo/1, get_foo/1, recurse_foo/1. 5 | 6 | load_it :- 7 | foo(1), 8 | foo(3), 9 | foo(7). 10 | 11 | /* 12 | % no backtrack get 13 | foo(X) \ get_foo(Y) <=> 14 | X = Y. 15 | get_foo(_) <=> fail. 16 | */ 17 | 18 | /* doesnt work 19 | foo(X), recurse_foo(Y) ==> 20 | Y = [ X | Rest ], 21 | recurse_foo(Rest). 22 | recurse_foo(_) <=> true. 23 | */ 24 | 25 | all_foos(L) :- 26 | nb_setval(all_foos_val, []), 27 | af(L). 28 | 29 | af(_) :- 30 | find_chr_constraint(foo(X)), 31 | nb_getval(all_foos_val, OldL), 32 | nb_setval(all_foos_val, [X | OldL]), 33 | fail. 34 | af(L) :- 35 | nb_getval(all_foos_val, L). 36 | 37 | :- chr_constraint gather_foos_together/1, get_thy_foos/1. 38 | 39 | foo(X) \ gather_foos_together(Old) <=> gather_foos_together([X | Old]). 40 | 41 | gather_foos_together(Foos), get_thy_foos(Y) ==> Foos = Y. 42 | % get_thy_foos(_) <=> true. don't do this 43 | 44 | 45 | :- chr_constraint get_foo_list/1. 46 | 47 | foo(X), get_foo_list(L) ==> 48 | L=[X|L1], get_foo_list(L1). 49 | get_foo_list(L) <=> L=[]. 50 | 51 | 52 | :- chr_constraint one_foo/1, collect_foo/1. 53 | 54 | % copy constraints to be collected 55 | foo(X), get_foo(_) ==> one_foo(X). 56 | get_foo(L) <=> collect_foo(L). 57 | 58 | % collect and remove copied constraints 59 | one_foo(X), collect_foo(L) <=> 60 | L=[X|L1], collect_foo(L1). 61 | collect_foo(L) <=> L=[]. 62 | 63 | 64 | :- chr_constraint i_delay/1, i_throw/1. 65 | 66 | i_throw(X) ==> X > 3 | writeln('fired'). % throws uninstantiated 67 | i_delay(_) ==> writeln('firstrule'). 68 | i_delay(X) ==> ground(X), X > 3 | writeln(fired). 69 | 70 | :- chr_constraint partial_bound/1. 71 | 72 | partial_bound(X) <=> nonvar(X) | is_list(X). 73 | 74 | 75 | :- chr_constraint card/1, straight/0. 76 | 77 | /* slow version 78 | card(A), card(B), card(C), card(D), card(E) ==> 79 | succ(A,B), 80 | succ(B,C), 81 | succ(C,D), 82 | succ(D,E) | straight. 83 | */ 84 | 85 | :- chr_constraint adj_pair/2, adj_triple/2. 86 | 87 | card(A), card(B) ==> succ(A,B) | adj_pair(A,B). 88 | adj_pair(A,B),card(C) ==> succ(B,C) | adj_triple(A, C). 89 | adj_pair(_,B),adj_triple(C,_) <=> succ(B,C) | straight. 90 | adj_triple(_,B),adj_pair(C,_) <=> succ(B,C) | straight. 91 | 92 | 93 | :- chr_constraint do_exprs_work/0, an_expr/1. 94 | 95 | do_exprs_work <=> an_expr(1+2). 96 | 97 | :- use_module(moduleb). 98 | 99 | :- chr_constraint in_a/0. 100 | 101 | in_a ==> in_b. 102 | 103 | :- chr_constraint call_private/0. 104 | 105 | call_private ==> moduleb:private_b. 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /constraintsystems.adoc: -------------------------------------------------------------------------------- 1 | Tutorial - CHR Constraint Handling Rules - Constraint Systems 2 | ============================================================= 3 | Anne Ogborn 4 | :Author Initials: AO 5 | :toc2: 6 | :icons: 7 | :numbered: 8 | :website: http://www.pathwayslms.com/swipltuts/ 9 | :theme: pathways 10 | 11 | 12 | link:/swipltuts/index.html[Up to All Tutorials] 13 | link:index.html[Introduction] 14 | link:basics.html[Basics] 15 | link:examples.html[Examples] 16 | link:constraintsystems.html[Constraint Systems] 17 | link:advanced.html[Advanced] 18 | link:final.html[Final] 19 | 20 | The material in this chapter depends on understanding constraint systems. 21 | 22 | If you want to understand making constraint systems, at least read the intro chapter of my link:/swipltuts/clpfd/clpfd.html[clp(fd) tutorial], the Prolog clpfd library notes, 23 | and the entries for attributed variables in the SWI-Prolog manual. 24 | 25 | .Review 26 | .A Simple Constraint System 27 | 28 | Review 29 | ------ 30 | 31 | To review - CHR constraints are **active** when added to the store. 32 | 33 | While there is an **active** constraint, CHR looks for a rule to fire, until no rule involving that constraint can fire. The constraint is then deactivated, and control returned. 34 | 35 | Since no rule was firing (at this stack level) before the constraint was added, only 36 | rules that contain the active constraint need be searched. 37 | 38 | There is an important exception to this rule. If the **guard** encounters `ground/1`, the **rule** 39 | (not the constraints) will **reactivate** when the argument of `ground/1` is **grounded**. 40 | 41 | ---- 42 | more_than_3(N) <=> ground(N), N > 3 | writeln('more'). 43 | more_than_3(N) <=> ground(N) | writeln('not more'), fail. 44 | 45 | ?- more_than_3(X), writeln('middle'), X = 2. 46 | middle 47 | not more 48 | false. 49 | ---- 50 | 51 | `nonvar/1` works the same way. 52 | 53 | I would note that in this respect the CHR implementation in SWI-Prolog is not very good. Many other 54 | Prologs also allow a variety of other predicates to act in this manner - the arithmetic comparison operators, 55 | and so on. 56 | 57 | If one of the reactivated rules fails, then the **unification fails in the original Prolog that caused the unification**. 58 | 59 | This allows us to build **constraint systems**. If you're not familiar with constraint systems, check out 60 | link:http://www.pathwayslms.com/swipltuts/clpfd/clpfd.html[my clp(fd) tutorial]. 61 | Note that the above code says 'middle', and only fails when X is bound. 62 | 63 | Since we can later change the constraint we can develop very efficient constraint checkers. 64 | If we apply `more_than_4` to X then we _subsume_ `more_than_3`. We can discard the weaker constraint. 65 | 66 | ---- 67 | more_than_4(X) \ more_than_3(X) <=> true. 68 | ---- 69 | 70 | A simple constraint system 71 | -------------------------- 72 | 73 | Let's make a constraint system that works over lists. 74 | 75 | We'll start by providing a variable that just ensures that a variable is only grounded to a list. 76 | 77 | ---- 78 | :- chr_constraint cl_list/1. <1> 79 | 80 | cl_list(X) <=> nonvar(X) | is_list(X). <2> 81 | 82 | go :- cl_list(X), <3> 83 | writeln('x not bound'), <4> 84 | X=[_]. <5> 85 | gofail :- cl_list(X), <3> 86 | writeln('x not bound'), <4> 87 | X=3. <6> 88 | ---- 89 | <1> define a constraint 90 | <2> if a `cl_list/1` is in the store, when X is partially grounded (nonvar), it must be a list. 91 | <3> test if it works. `cl_list/1` doesn't fire rule 2 when the constraint is added. 92 | <4> writeln to prove we get past the `cl_list/1` call. 93 | <5> Only when X is partially ground is the rule checked - and it's indeed a list. Notice if you run this that the constraint is not in the store, we used a simplification (`<=>`) rule. 94 | <6> This time, when we finally run the rule body it fails because 3 is not a list. 95 | 96 | Now let's add a minimum length constraint. 97 | 98 | ---- 99 | :- chr_constraint cl_min_len/2. 100 | 101 | cl_min_len(X, MinLen) <=> <1> 102 | nonvar(X), 103 | ground(MinLen) | <2> 104 | is_list(X), 105 | number(MinLen), 106 | length(X, Len), 107 | Len >= MinLen. <3> 108 | ---- 109 | <1> Use a simplification rule, since when it fires we don't need the constraint after. 110 | <2> Check that both are bound before firing the rule. 111 | <3> Now make sure the constraint holds when the rule fires. 112 | 113 | [Exercise] 114 | .Exercise - Ascending 115 | ===================================================================== 116 | Add a `cl_ascending` constraint that demands the elements in the list 117 | are in ascending order in the standard Prolog collating order. 118 | ===================================================================== 119 | 120 | Suppose a user does: 121 | 122 | ---- 123 | blah :- 124 | cl_min_len(X, 3), 125 | cl_min_len(X, 5), 126 | ... more ... 127 | ---- 128 | 129 | We don't really need the `cl_min_len(X, 3)` any more. 130 | 131 | ---- 132 | cl_min_len(X, A) \ cl_min_len(X, B) <=> A > B | true. 133 | ---- 134 | 135 | Now we don't have an extra constraint to check. 136 | 137 | 138 | [Exercise] 139 | .Exercise - Late binding the minimums 140 | ===================================================================== 141 | Test that this works if you don't have the minimums bound when you 142 | add the constraints, but only ground them later. 143 | ===================================================================== 144 | 145 | [Exercise] 146 | .Exercise - Build a constraint system 147 | ===================================================================== 148 | Project - 149 | Make a constraint system for numbers as booleans. 150 | 151 | * bits_on(Bits, X) % all bits set in Bits must be set in X 152 | * bits_off(Bits, X) % all bits 0 in Bits must be 0 in X 153 | 154 | At minimum make it: 155 | 156 | * replace multiple bits_on with one bits_on 157 | * replace multiple bits_off with one bits_off 158 | * fail if conflicting bits_on and bits_off are added 159 | * fail if X is grounded to other than an integer 160 | * fail if X doesn't match the constraints 161 | * remove all the constraints for X after they're no longer needed. 162 | 163 | ===================================================================== 164 | 165 | Conclusion 166 | ---------- 167 | 168 | Building constraint systems was the original application of CHR, and is still an important application. 169 | If you don't see yourself using constraint systems, consider that dynamic types are just constraint systems. 170 | 171 | You're now ready to move on to the 172 | link:advanced.html[Advanced] 173 | material. 174 | 175 | If you haven't worked through the 176 | link:examples.html[Examples] 177 | chapter, I suggest doing that before doing the 178 | advanced material. 179 | 180 | -------------------------------------------------------------------------------- /dev/addfooter.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/dev/addfooter.ai -------------------------------------------------------------------------------- /dev/blockdiagramlayout.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | page generation 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Folder 1 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | html generated 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | html generated 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | html generated 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | Resources included 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | json generated 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | page assembled 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | Request comes in 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | Routed to handler 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | Parameters extracted & verified 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | Back End 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | Static resources served 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | Browser 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | abstract paths 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | -------------------------------------------------------------------------------- /dev/comfy.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/dev/comfy.ai -------------------------------------------------------------------------------- /dev/comfymodule.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/dev/comfymodule.ai -------------------------------------------------------------------------------- /dev/systemsusingprolog.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/dev/systemsusingprolog.psd -------------------------------------------------------------------------------- /dev/twocamps.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/dev/twocamps.ai -------------------------------------------------------------------------------- /examples.adoc: -------------------------------------------------------------------------------- 1 | Tutorial - CHR Constraint Handling Rules - Examples and Patterns 2 | ================================================================ 3 | Anne Ogborn 4 | :Author Initials: AO 5 | :toc2: 6 | :icons: 7 | :numbered: 8 | :website: http://www.pathwayslms.com/swipltuts/ 9 | :theme: pathways 10 | 11 | 12 | link:/swipltuts/index.html[Up to All Tutorials] 13 | link:index.html[Introduction] 14 | link:basics.html[Basics] 15 | link:examples.html[Examples] 16 | link:constraintsystems.html[Constraint Systems] 17 | link:advanced.html[Advanced] 18 | link:final.html[Final] 19 | 20 | This chapter covers no truly new material, but should help you find some useful ways to use CHR. 21 | 22 | 23 | .Useful patterns 24 | ** get_foo 25 | ** constraint system 26 | ** adjacency 27 | ** set semantics 28 | ** uniqueness 29 | ** generate and filter 30 | ** actions and state 31 | ** collection/aggregation 32 | ** speeding up graph traversal 33 | ** backtracking for labeling 34 | . Implementing Things in CHR 35 | 36 | 37 | Useful Patterns 38 | --------------- 39 | 40 | get_foo 41 | ~~~~~~~ 42 | 43 | This pattern is used to get a list of constraints from the store. 44 | 45 | It was covered in 'getting', but is largely repeated here. 46 | 47 | The trick is to pass in an unbound variable with `get_foo`, and 48 | then bind the variable in rule bodies. 49 | 50 | ---- 51 | :- use_module(library(chr)). 52 | 53 | :- chr_constraint foo/1,one_foo/1, collect_foo/1, get_foo/1. <1> 54 | 55 | load_it :- <2> 56 | foo(1), 57 | foo(3), 58 | foo(7). 59 | 60 | % copy constraints to be collected 61 | foo(X), get_foo(_) ==> one_foo(X). <3> 62 | get_foo(L) <=> collect_foo(L). <4> 63 | 64 | % collect and remove copied constraints 65 | one_foo(X), collect_foo(L) <=> <5> 66 | L=[X|L1], collect_foo(L1). 67 | collect_foo(L) <=> L=[]. <6> 68 | 69 | go(X) :- load_it, get_foo(X). <7> 70 | ---- 71 | 72 | <1> There are four moving pieces. 73 | * `foo/1`, the constraint. 74 | * `one_foo/1`, a temporary copy of the constraint 75 | * `collect_foo/1`, a place to hold the list built up so far 76 | * `get_foo/1` the API to the outside Prolog 77 | <2> Let's load some constraints 78 | <3> We'll copy all the constraints, then delete the copies as we collect them into the list 79 | <4> When we start we swap `get_foo` for `collect_foo`, passing along the argument. 80 | <5> we discard a `one_foo`, and the current `collect_foo`. We bind `collect_foo`'s argument **L** 81 | to a **hole list**, a list whose final element is the unbound L1. We then add a new `collect_foo` with L1 to fill in the rest of the list. 82 | <6> Eventually we run out of `one_foo`'s. We transform the **hole list** to a normal list by 'plugging the hole' with an empty list. 83 | <7> convenience predicate to run the system. 84 | 85 | Constraint System 86 | ~~~~~~~~~~~~~~~~~ 87 | 88 | See the chapter on constraint systems. 89 | 90 | Adjacency 91 | ~~~~~~~~~ 92 | 93 | We may have an ordered list of items, and want to put them in a constraint store while preserving their 94 | order information. In particular, we might want to preserve adjacency. 95 | 96 | Here are a few strategies 97 | 98 | Keep an index 99 | ^^^^^^^^^^^^^ 100 | 101 | Store constraints as `foo(Index, Data)`. Check adjacency as 102 | 103 | ---- 104 | foo(I, Data1), foo(J, Data2) ==> succ(I, J) | something(Data1, Data2). 105 | ---- 106 | 107 | Keep Index Pairs 108 | ^^^^^^^^^^^^^^^^ 109 | 110 | Store constraints as `foo(Index, Next, Data)` where Next is Index + 1. 111 | 112 | ---- 113 | foo(_, I, Data1), foo(I, _, Data2) ==> something(Data1, Data2). 114 | ---- 115 | 116 | This takes more memory but runs faster. 117 | 118 | This strategy also allows a form of **bottom up parsing** where you replace raw tokens with 119 | progressively more 'cooked' tokens. Suppose we have some tokens already classified as `word_char(Char)`, 120 | we can assemble words as: 121 | 122 | ---- 123 | token(WordIndex, Letter, word(Word)), token(Letter, Next, word_char(B)) <=> 124 | append(Word, [B], NewWord), 125 | token(WordIndex, Next, word(NewWord)). 126 | token(WordIndex, Next, word_char(A)) <=> token(WordIndex, Next, word([A])) 127 | ---- 128 | 129 | Set Semantics 130 | ~~~~~~~~~~~~~ 131 | 132 | Sometimes it's useful to give a CHR constraint set semantics instead of the default multiset semantics. 133 | 134 | Model of light in a room 135 | 136 | ---- 137 | :- chr_constraint turn_on/0, turn_off/0, light_on/0. 138 | 139 | turn_on <=> light_on. 140 | turn_off, light_on <=> true. 141 | ---- 142 | 143 | The problem is, if the light is already on when turn_on occurs, we get two `light_on` constraints 144 | in the store. Not what we want - `turn_off` won't work properly. 145 | 146 | Add a rule 147 | 148 | ---- 149 | light_on \ light_on <=> true. 150 | ---- 151 | 152 | Uniqueness 153 | ~~~~~~~~~~ 154 | 155 | Want to enforce uniqueness? 156 | 157 | Heather has two mommies, but only one biological mother. 158 | 159 | ---- 160 | bio_mom_of(X,Y) \ bio_mom_of(Z, Y) <=> X = Z. 161 | ---- 162 | 163 | First, we are demanding that if there are two `bio_mom` constraints for a child, 164 | then they must be for the same person. That's a long winded way of saying "only one bio_mom". 165 | 166 | Second, we are enforcing set semantics - if they are the same, we discard one. 167 | 168 | Generate and Filter 169 | ~~~~~~~~~~~~~~~~~~~ 170 | 171 | It is often easier to generate a set of something and then filter out items not in the set. 172 | 173 | Here's a (rather inefficient) prime number generator: 174 | 175 | ---- 176 | :- chr_constraint init/1,num/1. 177 | % primes 178 | 179 | % generate numbers from 1-n 180 | init(N) ==> num(1). 181 | init(N), num(M) ==> N > M | succ(M, NewM), num(NewM). 182 | num(N) \ init(N) <=> true. 183 | 184 | % remove the non-primes. 185 | num(A) \ num(B) <=> B > A, A > 1, B mod A =:= 0 | true. 186 | ---- 187 | 188 | Actions and State 189 | ~~~~~~~~~~~~~~~~~ 190 | 191 | I find it useful to distinguish _actions_, constraints in the store to set off behavior, from _state_. 192 | 193 | In the above prime number example, we know we should make num/1 constaints when init/1 exists. 194 | 195 | The general pattern is: 196 | 197 | 198 | . Outside agency (prolog or other CHR) adds the 'ignition' constraint 199 | . Rules fire until some process completes 200 | . The 'ignition' constraint is removed. 201 | 202 | Collection/Aggregation 203 | ~~~~~~~~~~~~~~~~~~~~~~ 204 | 205 | If you want to **maintain** an aggregate total, you can detect adding and removing 206 | the constraint: 207 | 208 | ---- 209 | % add and remove foo's with numeric argument, keeping running total 210 | :- chr_constraint foo/1, total_foo/1, unfoo/1. 211 | 212 | total_foo(A), total_foo(Total) <=> NewTotal is A + Total, total_foo(NewTotal). 213 | foo(A) ==> total_foo(A). 214 | 215 | unfoo(A), foo(A), total_foo(Total) <=> NewTotal is Total - A, total_foo(NewTotal). 216 | ---- 217 | 218 | You can do aggregation / foldl / collection in the same way get_foo works. 219 | 220 | ---- 221 | :- use_module(library(chr)). 222 | 223 | :- chr_constraint foo/1,one_foo/1, sum_foo/1, do_sum_foo/0. <1> 224 | 225 | load_it :- <2> 226 | foo(1), 227 | foo(3), 228 | foo(7). 229 | 230 | % copy constraints to be collected 231 | foo(X), do_sum_foo ==> one_foo(X). <3> 232 | do_sum_foo <=> sum_foo(0). <4> 233 | 234 | % collect and remove copied constraints 235 | one_foo(X), sum_foo(N) <=> <5> 236 | NN is N + X, 237 | sum_foo(NN). 238 | 239 | go(X) :- load_it, do_sum_foo, find_chr_constraint(sum_foo(X)). <6> 240 | ---- 241 | 242 | <1> There are four moving pieces. 243 | * `foo/1`, the constraint. 244 | * `one_foo/1`, a temporary copy of the constraint 245 | * `sum_foo/1`, a place to hold the list built up so far 246 | * `do_sum_foo/0` the action constraint that sets it off 247 | <2> Let's load some constraints 248 | <3> We'll copy all the constraints, then delete the copies as we sum them 249 | <4> Next we create `sum_foo`. 250 | <5> we discard a `one_foo`, and the current `sum_foo`. We add a new `sum_foo` with the new sum. Eventually we run out of `one_foo`'s and sum_foo contains the total. 251 | <6> convenience predicate to run the system. 252 | 253 | 254 | Speeding Up Graph Traversal 255 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 256 | 257 | If you have a small graph you traverse many times, it may be worth adding edges 258 | to directly go to indirectly reachable nodes. 259 | 260 | ---- 261 | % make all connected edges 262 | % if you have a-b b-c c-d 263 | % add a-c b-d a-d 264 | 265 | :- chr_constraint node/2. 266 | node(A,B) \ node(A,B) <=> true. % anti-cycle 267 | 268 | node(A,B), node(B,C) ==> node(A,C). 269 | 270 | ?- node(1,2), node(2,3), node(3,1). 271 | 272 | query(5, 2, X). 273 | 274 | :- chr_constraint query(+, +, -). % uses material from advanced chapter 275 | node(A,D), node(B,E) 276 | \ query(A,B,C) 277 | <=> C is E + D. 278 | ---- 279 | 280 | Backtracking for Labeling 281 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 282 | 283 | Constraint systems usually provide a `label` predicate to force grounding of all variables. 284 | 285 | CHR's reversal on backtracking provides a nice way of writing a labeling predicate for your 286 | constraint system. 287 | 288 | Implementing Things in CHR 289 | -------------------------- 290 | 291 | Here's the list of applications we started this tutorial with. Now that you have some understanding of CHR, 292 | let's look at how we'd implement most of these. I omitted a few as being obvious or unedifying. 293 | 294 | writing constraint systems 295 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 296 | 297 | [[subsume]] 298 | When constraint B subsumes constraint A, discard A for better performance 299 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 300 | 301 | ---- 302 | must_be_more_than(X, Y) \ must_be_more_than(X, Z) <=> Y >= Z | true. 303 | ---- 304 | 305 | When there is a route from power to ground, we have a short 306 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 307 | 308 | This assumes the student knows a little about electronic circuitry. 309 | 310 | ---- 311 | :- chr_constraint at_voltage/2, short/1, wire/2. 312 | 313 | % remove duplicates 314 | short(T) \ short(T) <=> true. 315 | % prevent an infinite loop 316 | at_voltage(V, T) \ at_voltage(V, T) <=> true. 317 | % if we are holding a pin at two different voltages there's a short 318 | at_voltage(V1, T), at_voltage(V2, T) ==> V1 \= V2 | short(T). 319 | % connections to a pin from a voltage source hold that pin at the source 320 | % vcc is the power supply voltage, gnd is ground 321 | wire(Term, vcc) ==> at_voltage(vcc, Term). 322 | wire(Term, gnd) ==> at_voltage(gnd, Term). 323 | wire(vcc, Term) ==> at_voltage(vcc, Term). 324 | wire(gnd, Term) ==> at_voltage(gnd, Term). 325 | % if T1 and T2 have a wire between them and one end is held at a voltage, 326 | % the other end is too 327 | wire(T1, T2), at_voltage(V, T1) ==> at_voltage(V, T2). 328 | wire(T1, T2), at_voltage(V, T2) ==> at_voltage(V, T1). 329 | ---- 330 | 331 | Build a constraint system like clp(fd) 332 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 333 | 334 | Obviously this is beyond the scope of a tutorial, but there are examples in the chapter on 335 | link:constraintsystems.html[Constraint Systems] 336 | 337 | Build a type system 338 | ~~~~~~~~~~~~~~~~~~~ 339 | 340 | A (dynamic) type is just a constraint to a specific type. 341 | 342 | ---- 343 | must_be_int(X) <=> ground(X) | integer(X). 344 | ---- 345 | 346 | alternatively 347 | 348 | ---- 349 | must_be_int(X, _) <=> ground(X), integer(X) | true. 350 | must_be_int(X, Context) <=> ground(X) | throw(error(type_error(integer, X), Context)). 351 | ---- 352 | 353 | Constraint systems are often far faster than hand rolled algorithms 354 | See 355 | xref:subsume[When constraint B subsumes constraint A, discard A for better performance] 356 | 357 | Making programs where things transform into things 358 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 359 | 360 | A GUI where a button changes appearance when clicked 361 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 362 | 363 | Here's code for a button. You'd have to add an action triggered by 364 | appearance(B, Appearance) to trigger some prolog that physically changed the button. 365 | 366 | You can wire behaviors to buttons by rules triggered by do_action. 367 | 368 | ---- 369 | % came down over B 370 | button(B) \ appearance(B, up), mousedown(B) <=> appearance(B, active). 371 | % mouse is down and user moved off the button 372 | button(B) \ appearance(B, active), mouseleft(B) <=> appearance(B, up), pending_button(B). 373 | % came back on - we stored pending_button above so we only activate the original down button 374 | button(B) \ appearance(B, up), mouseentered(B), pending_button(B) <=> appearance(B, active). 375 | % mouseup with the mouse over the button 376 | % dispatch the action and start an animation. the start_animation prolog predicate adds 377 | % tick to the store every 0.2 seconds for 2 frames 378 | button(B) \ appearance(B, active), mouseup <=> do_action(B), appearance(B, click0), start_animation(2, 0.2). 379 | % mouseup with the mouse not over button 380 | button(B) \ pending_button(B), mouseup <=> true. 381 | % handle the animation 382 | appearance(B, click0), tick <=> appearance(B, click1). 383 | appearance(B, click1), tick <=> appearance(B, up). 384 | ---- 385 | 386 | A game where a a spaceship changes appearance after it is hit, fires it's rockets, etc. 387 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 388 | 389 | Here's a bit of CHR code that processes hits on spaceships, and some refactors. 390 | 391 | ---- 392 | % ignore hits on dead ships 393 | ship(S), dead(S) \ hit(S, _) <=> true. 394 | % die if ship falls below 0 health 395 | ship(S) \ health(S, H), hit(S, Damage), appearance(S, _) <=> 396 | H - Damage > 0 | 397 | appearance(S, damaged), 398 | NH is H - Damage, 399 | health(S, NH). 400 | ship(S) \ health(S, H), hit(S, Damage) <=> 401 | H - Damage =< 0 | 402 | dead(S). 403 | ship(S), dead(S) \ appearance(S, _) <=> 404 | A \= dead | 405 | appearance(S, dead). 406 | ---- 407 | 408 | There's a better CHR pattern. The ship is either `ok`, or `damaged`, or `dead`. 409 | So better to have `health_state(S, damaged)` than `dead(S)`. 410 | 411 | Secondly, I decided already to separate the ship's state from it's appearance. 412 | If later we want to have a power up that makes the player's ship look dead, so 413 | enemies ignore it, we need to have the appearance separate from the 'model'. Good 414 | MVC pattern. 415 | But we have the appearance code mixed in with the model code. Let's separate concerns. 416 | 417 | A third pattern is that I check S is actually a ship. If only ships have health or health_state, I 418 | can remove that check. Alternatively, I can have `ship_health_state` instead of `health_state` 419 | so we're assured this is just a ship. 420 | 421 | lastly, we can separate the concern of updating the ship's health from it's state. 422 | 423 | Let's rewrite it with those changes. 424 | 425 | ---- 426 | % ships start out ok state with 20 hit points 427 | ship(S) ==> health_state(S, ok), health(S, 20). 428 | 429 | % ignore hits on dead ships 430 | ship(S), health_state(S, dead) \ hit(S, _) <=> true. 431 | 432 | % take damage 433 | ship(S) \ health(S, H), hit(S, Damage) <=> 434 | NH is H - Damage, 435 | health(S, NH). 436 | 437 | % die if ship falls below 0 health 438 | ship(S), health(S, H) \ health_state(S, State) <=> 439 | H =< 0, State \= dead | 440 | health_state(S, dead). 441 | 442 | % for now appearance is just state 443 | ship(S), health_state(S, State) ==> appearance(S, State). 444 | appearance(S, dead) \ appearance(S, _) <=> true. 445 | 446 | ---- 447 | 448 | This version, in my opinion, comes much closer to **expressing our design intent**. 449 | 450 | 451 | A simulation like a virtual chemistry lab, where adding A and B to the beaker makes C 452 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 453 | 454 | This is essentially the salt water example we started with. 455 | 456 | 457 | An interactive fiction game, where the character moves from room to room 458 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 459 | 460 | Here's a simple game where the player can move among 3 rooms. They must pick up 461 | a key to get into the library. For simplicity, you can only go east, west, and pick_up (the key). 462 | 463 | ---- 464 | :- chr_constraint init/0, description/2, player/1, pick_up/0, key/0, picked_up_key/0, look/0, east/0, west/0. 465 | 466 | % clear any previous state then initialize game state 467 | init \ key <=> true. 468 | init \ player(_) <=> true. 469 | init \ description(_,_) <=> true. 470 | init <=> description(bedroom, 'An ordinary bedroom. The bed is not made.'), 471 | description(living_room, 'Bob\'s living room. The picture matches his sofa. There is a key here.'), 472 | description(library, 'Bob\'s library. Mostly Harlequin romances and Readers Digest.'), 473 | player(bedroom). 474 | 475 | % moves 476 | player(bedroom), east <=> player(living_room). 477 | player(living_room), east, key <=> player(library). 478 | player(library), west <=> player(living_room). 479 | player(living_room), west <=> player(bedroom). 480 | player(living_room), key \ pick_up <=> writeln('You already have the key.'). 481 | player(living_room) \ pick_up <=> key, picked_up_key. 482 | east <=> writeln('You can\'t go east here.'). 483 | west <=> writeln('You can\'t go west here.'). 484 | pick_up <=> writeln('Nothing to pick up here.'). 485 | 486 | % printing 487 | 488 | % make the living_room description change when the key's picked up 489 | % picked_up_key prevents an infinite loop 490 | key \ picked_up_key, description(living_room, _) <=> description(living_room, 'Bob\'s living room. The picture matches his sofa.'). 491 | 492 | % print the player's location 493 | player(Loc), description(Loc, Desc) ==> writeln(Desc). 494 | 495 | % handle look command 496 | 497 | look, player(Loc), description(Loc, Desc) ==> writeln(Desc). 498 | look, key ==> writeln('You are carrying a key.'). 499 | look <=> true. 500 | ---- 501 | 502 | A few subtleties - The **look** command uses the order to ensure we process everything we need - print out 503 | the player's location description, and then tell the player they're carrying the key if they are. 504 | Then we destroy the look command. 505 | 506 | [[bottomup]] 507 | bottom up recognizers - input characters to words to clauses to sentences to paragraphs. 508 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 509 | 510 | DCGs are what immediately comes to mind when we think about parsing in Prolog. 511 | 512 | But the parsers generated can have issues with left recursion, and with performance. Additionally, they 513 | are most natural for languages like computer languages that are defined recursively. 514 | 515 | [quote, David Warren, XSB Book] 516 | ____ 517 | "It is for this reason that some people respond to the claim that _You get a parser for free with Prolog_ with _Maybe, but it's not a parser I want to use_. " 518 | ____ 519 | 520 | CHR makes it easy to do parsing _bottom up_, building up words, then sentences, and so on. 521 | 522 | As a simple example, let's build a CHR program that builds up sentences as lists of words. 523 | For convenience, we'll convert the words to atoms. 524 | 525 | As is often useful in Prolog, we'll build the words and sentences up in **reverse**, and flip them with 526 | `reverse\2` at the end. 527 | 528 | Some outside Prolog will repeatedly add char/1 letters to the store. The argument is the ASCII value of the 529 | character. 530 | 531 | Our example just builds up sentences in the 532 | store and prints them out. We'll also lowercase incoming text and discard punctuation, a common NLP need. 533 | 534 | Our example defines a sentence as anything followed by a period. 535 | 536 | ---- 537 | :- chr_constraint sentence_atom/1, char/1, word_done/0, letter/1, partial_word/1, partial_sentence/1, sentence/1, sentence_done/0. 538 | 539 | sentence_atom(SentenceAtom) <=> 540 | partial_sentence([]), 541 | atom_codes(SentenceAtom, Codes), 542 | maplist(char, Codes). 543 | 544 | % is it a letter? 545 | char(X) <=> X >= 0'a , X =< 0'z | letter(X). 546 | char(X) <=> X >= 0'A , X =< 0'Z | letter(X). 547 | char(0'.) <=> word_done, sentence_done. 548 | char(_) <=> word_done. % everything else ends words 549 | 550 | letter(X), partial_word(List) <=> 551 | partial_word([X | List]). 552 | letter(X) <=> partial_word([X]). 553 | 554 | word_done, partial_word(List), partial_sentence(Sentence) <=> 555 | partial_word_word(List, Word), 556 | partial_sentence([Word | Sentence]). 557 | word_done <=> true. % eg comma followed by space we just ignore the space 558 | 559 | sentence_done, partial_sentence(S) <=> 560 | reverse(S, Sentence), 561 | sentence(Sentence). 562 | 563 | partial_word_word(List, Word) :- 564 | reverse(List, CodeWord), 565 | atom_codes(Word, CodeWord). 566 | ---- 567 | 568 | Notice that I get rid of things from the store as soon as they're incorporated in something bigger. 569 | Letters are included in the current partial word and **then discarded**. 570 | 571 | Note also that I uses `partial_word_word/2` to convert the list - there seemed little point in doing 572 | this in CHR. The Prolog is both more efficient and easier to write. 573 | 574 | [[postcomplex]] 575 | A web application where POST requests modify the store, subject to complex constraints. 576 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 577 | 578 | Of course a web application is a bit complex for a tutorial. I'll refer you to an example. 579 | 580 | link:http://swi-prolog.org[SWI-Prolog] 581 | occasionally organizes teams to participate in 582 | link:http://ldjam.com[Ludum Dare] 583 | ,a large international game jam competition. 584 | 585 | In summer 2019 the team built a fun little game where you draw childs drawing 'houses' to save a little pig 586 | from a bad wolf. The 587 | link:https://github.com/SWI-PrologTeamLudumDare32/LudumDare45[source code is here]. 588 | 589 | The front end of the game is in 590 | link:https://snap.berkeley.edu/[Snap!] 591 | , a visual programming environment of the 'drag blocks' type. 592 | 593 | an alternative to setting up Snap! locally (which is easy) is to use curl to exercise the system. 594 | 595 | A few things to observe. 596 | 597 | * Single threaded - all CHR happens in a single thread. We wanted a single constraint store for this application and the constraint store is **per thread**. 598 | * some parsing on the Prolog side. Our client (the **Snap!** game) had crude HTTP support (why we use GET requests). It was easier in practice to do the first bit of parsing in prolog, so we did. 599 | 600 | The code starts by decoding the line segments the user has drawn from the input stream of numbers. 601 | 602 | Then it starts identifying 'bigger' pieces. It tests geometry to identify vertical and horizontal lines. 603 | A vertical and a horizontal line that are close at a corner make a 'corner'. A pair of 'corner's make a 'box'. 604 | two slanted lines that touch at the end make a triangle, which might have a third, horizontal or vertical side. 605 | A triangle atop a box is a 'house'. The code has the start of recognizing 'rocket'. 606 | 607 | Note that the 'house' code is actually quite short. The more things we recognize, the more we CAN recognize. 608 | 609 | Note also that it can become tricky - if I replace the 3 lines of the 'roof' with a 'triangle', I lose the top of my 'box'. 610 | And if I recognize a house, well, isn't a vertical rocket a tall thin house with fins? This sort of competing recognition 611 | can become a real software design issue, but a bit of care makes it tractable. 612 | 613 | Notice that the application needn't be a game. the system could be maintaining 'session' term state for a business application. 614 | 615 | The button example above shows a common need with web applications. State is held on the server, and then we want to send 616 | only the changes to the client. 617 | 618 | Suppose we have a web page with various lists of items, say a list of methods of payment. When the browser sends a request to add a method, we respond telling the client side JS to rebuild the payment method list. If they just change an item within one payment method, we tell the client to rebuild only that item. CHR is excellent for building this sort of coordination between change and current state. 619 | 620 | I personally think there's a lot of possibility in melding CHR and tau-Prolog to make a fully declarative web application development system. 621 | 622 | Writing **Recognizers** 623 | ~~~~~~~~~~~~~~~~~~~~~~~ 624 | 625 | A **Recognizer**, for our purposes, is a CHR program that takes some input and 'finds' a pattern within it. 626 | 627 | For an example of doing more general recognition, see the game referenced in 628 | xref:postcomplex[A web application where POST requests modify the store, subject to complex constraints.] 629 | 630 | A traditional bottom up parser 631 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 632 | 633 | Isn't your 'recognizer' just a parser? 634 | 635 | Yes, it is, but we associate parsers so strongly with objects that are ordered, like an input string, that I've used 636 | a different word for 'find things in this mass of unordered data'. 637 | 638 | For an example of doing traditional parsing in CHR, see 639 | xref:bottomup[bottom up recognizers - input characters to words to clauses to sentences to paragraphs.] 640 | above. 641 | 642 | A computer vision system that recognizes simple objects like a chimney, a door, etc. and combines them to make 'house'. 643 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 644 | 645 | When I wrote this I was imagining starting with a raster image like a photograph in which we want to find various objects. 646 | 647 | For example, say we want to find houses, and on them, doors. 648 | 649 | It's rather hard to build a door recognizer using machine learning - they are, like many things, just rectangles, and 650 | have a large variety of context (porch, bushes, etc). But it's easier to build a house recognizer. So we build a good house 651 | recognizer and a bad door recognizer, and maybe a passable window recognizer and a good chimney recognizer. 652 | 653 | And we take the output of those, and combine them in CHR, and use symbolic recognition similar to the drawing game, and if 654 | the combination of windows, door, chimney, and 'house' doesn't make sense, we can suspect a false positive. 655 | 656 | Writing Expert Systems 657 | ~~~~~~~~~~~~~~~~~~~~~~ 658 | Most expert systems are just forward chaining inference engines. 659 | 660 | The classic 'room layout' example is an easy exercise in CHR. In this expert system we find solutions 661 | to room arrangements that satisfy some constraints, like the sofa must face the TV, the lamp must be near 662 | a socket and the reading chair, and so on. 663 | 664 | Such an expert system uses the **generate and filter** strategy. Code, in CHR or Prolog, generates a room configuration, 665 | and the CHR system confirms that it is a valid arrangment, or **fails**, making us backtrack and try again. 666 | Here's a simple version that enforces a reasonable arrangement for my own living room. 667 | 668 | ---- 669 | :- chr_constraint place/2. 670 | 671 | find_placement :- 672 | member(Sofa, [n, s, e, w]), 673 | place(sofa, Sofa), 674 | member(TV, [n, s, e, w]), 675 | place(tv, TV), 676 | member(Desk, [n, s, e, w]), 677 | place(desk, Desk), 678 | format('place sofa on ~w, tv on ~w, desk on ~w~n', [Sofa, TV, Desk]). 679 | 680 | place(sofa, n) ==> fail. % door to kitchen doesn't leave enough space for sofa 681 | place(sofa, s) ==> fail. % outside door and puja niche don't leave enough space for sofa 682 | place(sofa, X), place(tv, Y) ==> opposite(X,Y). % demand sofa and tv are on opposite walls 683 | place(desk, n) <=> fail. % don't place the desk on the north wall, no room. 684 | 685 | opposite(n, s). 686 | opposite(s, n). 687 | opposite(e, w). 688 | opposite(w, e). 689 | ---- 690 | 691 | We are depending on the fact that when the body fails the original call to add a CHR constraint fails. 692 | So, for example, we start by trying to add the sofa on the north. The first CHR rules overrides us, 693 | we can't put the sofa on the north. 694 | It really doesn't matter if we use `==>` or `<=>` here. In the end, there won't be a change to the constraint 695 | store if it fails. 696 | 697 | Notice that this is much more efficient than an equivalent **Prolog generate and test** strategy. 698 | placing the TV at a location that's not opposite immediatey is a fail, we don't need to try to place 699 | the desk. 700 | 701 | recognizers in expert systems 702 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 703 | 704 | Suppose we have a medical expert system. 705 | 706 | link:https://en.wikipedia.org/wiki/Acute_abdomen[Acute abdomen] 707 | is a serious medical emergency with many possible causes. 708 | Before we can proceed to a diagnosis, we have to recognize that the patient does indeed have _acute abdomen_. 709 | 710 | * patient must have severe abdominal pain 711 | * if the patient had a sudden onset, then it's _acute abdomen_. 712 | * if the patient report of pain onset is vague, these observations improve the diagnosis 713 | ** bloody stools 714 | ** constipation 715 | ** fever 716 | 717 | Once we've recognized _acute abdomen_ we can reason about possible causes and figure out what tests to run first. 718 | (**obviously**, don't use this CHR tutorial for medical advice. See a doctor.) 719 | 720 | resource utilization - I have an extra gate on this IC and need one for this. 721 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 722 | 723 | "Linear Logic" is a logic of finite resources. In a conventional logic, there is an ever increasing amount of 724 | facts known. 725 | 726 | However, when reasoning about the real world, it is convenient to think about a world where making a thing consumes another thing. 727 | 728 | 729 | ---- 730 | cake_pan, oven \ cake_mix, eggs, water <=> cake. 731 | ---- 732 | 733 | When we bake a cake, we destroy the mix of ingredients from which it's made, but not the cake pan or oven. 734 | 735 | CHR is an excellent method of reasoning about such problems. 736 | 737 | A classic linear logic problem is to simulate the action of a soda machine. 738 | 739 | ---- 740 | quarter, quarter, quarter <=> soda. 741 | ---- 742 | 743 | If you put in 3 quarters, you get back a soda. 744 | 745 | If done in a **generate and filter** pattern, we can find solutions to complex resource management problems. 746 | 747 | "design constraint" systems - like the checker tool before submitting a PCB design 748 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 749 | 750 | Suppose we have a design constraint that every pin has at least 2 wires connected, as it might be in an automated 751 | wire-wrap board. We can check a constraint like this 752 | 753 | ---- 754 | design_check, wire(T, _) ==> pin_not_checked(T). 755 | design_check, pin_not_checked(T) \ pin_not_checked(T) <=> true. % get rid of duplicates 756 | design_check, wire(T, A), wire(T, B) \ pin_not_checked(T) <=> A \= B | true. 757 | design_check, pin_not_checked(T) <=> fail. % or do whatever's appropriate when the design fails 758 | ---- 759 | 760 | We need `design_check` because the design won't be valid until we've added all the wires. 761 | 762 | Implication 763 | ~~~~~~~~~~~ 764 | 765 | Things imply other things. This is at the heart of the forward chaining nature of CHR. 766 | 767 | Tech Tree 768 | ^^^^^^^^^ 769 | 770 | An AI player for a game with a 'tech tree', where players must build the **tank_factory** to build tanks. Seeing a **tank** means they have the **tank_factory**. 771 | 772 | This example is available in the examples folder of this tutorial. 773 | 774 | Reasoning about mutable state 775 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 776 | 777 | A satellite imagery analysis system sees a combine in a field. That implies the crop is being harvested. 778 | During harvest season the local grain elevator will be full. A train will come to empty the elevator. 779 | 780 | PCB Traces 781 | ^^^^^^^^^^ 782 | 783 | ---- 784 | wire(T1, T2), location(T1, X1, Y1), location(T2, X2, Y2) ==> 785 | outline_trace(X1, Y1, X2, Y2, List), % Prolog to do the geometry, not shown 786 | polygon(List). 787 | ---- 788 | 789 | In a real example, there would be rules to union the polygon with others. 790 | 791 | 792 | Search 793 | ~~~~~~ 794 | 795 | Flexibly turning normalized data that has to be accessed via joins into single lookup data. 796 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 797 | 798 | Suppose we have some constraints that resemble normalized database tables. 799 | 800 | ---- 801 | employee(12345, 'Sally Smith', 1234567890). 802 | position(12345, manager, outside_sales). 803 | salary(12345, 69400, 0.1). 804 | ---- 805 | 806 | While this usually is the best way to represent the data, we sometimes want to examine data in 807 | denormalized form. 808 | 809 | ---- 810 | employee(Num, Name, SSN), position(Num, Position, Dept) ==> emp(Num, Name, SSN, Position, Dept). 811 | ---- 812 | 813 | Now searches of the database don't require rules with multiple heads. 814 | 815 | Of course this is a memory/speed tradeoff. 816 | 817 | Conclusion 818 | ---------- 819 | 820 | Some additional examples are available at the 821 | link:http://chr.informatik.uni-ulm.de/~webchr/[WebCHR Online CHR Tool] 822 | 823 | It's impossible to learn CHR without doing a lot of work in it. Hopefully this chapter has given 824 | you some experience with real CHR programs. 825 | 826 | You're now ready to move on to the 827 | link:advanced.html[Advanced] material. 828 | 829 | If you haven't worked through the 830 | link:constraintsystems.html[Constraint Systems] 831 | chapter, I suggest doing that before doing the advanced material. 832 | 833 | 834 | 835 | 836 | -------------------------------------------------------------------------------- /examples/techtree.pl: -------------------------------------------------------------------------------- 1 | /* This example solves questions about 2 | * "tech trees" using Constraint Handling Rules 3 | * 4 | * Tech Trees are a common game mechanic where the player, 5 | * or AI opponent, must build a specific building or unit 6 | * before they can build another. 7 | * Seeing a unit, the opposing player can then infer that 8 | * their opponent possesses all the units needed to 9 | * So, for example, if building tanks requires the tank factory, 10 | * and the tank factory requires the foundry, then if we see a tank 11 | * we can infer they have the foundry. 12 | * 13 | * Our logic doesn't take into account later destruction of units. 14 | * 15 | * Usage is slightly complicated by the fact that returning to the 16 | * top level clears the constraint store. Hence provision of the i_saw 17 | * convenience predicate. 18 | * 19 | * CHR syntax, since I can never remember it: 20 | * name @ retained \ discarded <=> guard | head,body. Simpagation 21 | * name @ discarded <=> guard | head, body. Simplification 22 | * name @ retained ==> guard | head, body. Propagation 23 | * 24 | * Usage: 25 | * ?- init, i_saw(boat). 26 | * ?- init, i_saw([boat, knight]). 27 | * 28 | * stepping in the chr_debugger is useful 29 | * 30 | * ?- chr_trace,init,i_saw(boat). 31 | * 32 | * useful references 33 | * https://dtai.cs.kuleuven.be/CHR/files/tutorial_iclp2008.pdf 34 | * https://dtai.cs.kuleuven.be/CHR/ 35 | * https://dtai.cs.kuleuven.be/CHR/webchr.shtml 36 | * https://www.swi-prolog.org/pldoc/man?section=chr 37 | * 38 | * Copyright (c) 2019 Anne Ogborn 39 | * released under the accompanying MIT license 40 | */ 41 | 42 | 43 | :- use_module(library(chr)). 44 | 45 | % CHR constraints must be defined using this directive 46 | :- chr_constraint 47 | saw/1, % I saw a unit of this type 48 | has/1, % I can infer enemy has a unit of this type 49 | can_build/1, % I can infer enemy can build a unit of this type 50 | can_build_if/2, % enemy can build a unit of this type if arg2 list all exist 51 | needs/2, % game rule, to build arg1, all of list arg2 must exist 52 | reset/0. % reset the game world 53 | 54 | % reset the game elements 55 | reset \ saw(_) <=> true. 56 | reset \ has(_) <=> true. 57 | reset \ can_build(_) <=> true. 58 | reset \ needs(_, _) <=>true. 59 | reset \ can_build_if(_, _) <=> true. 60 | reset <=> true. 61 | 62 | % 63 | % set the initial state of the constraint store, 64 | % with the game dependencies and two initial peasants 65 | % 66 | % to make barracks must have peasant, etc. 67 | init :- 68 | needs(barracks, [peasant]), 69 | needs(stable, [peasant, barracks]), 70 | needs(dock, [peasant, barracks]), 71 | needs(swordsman, [barracks]), 72 | needs(knight, [stable]), 73 | needs(boat, [dock, peasant]), 74 | saw(peasant), % we 'saw' the peasant 75 | saw(peasant). % because game starts with 2 peasants 76 | 77 | 78 | % enforce set semantics for various things 79 | % once we know they have a boat, we don't want to add 80 | % that again 81 | saw(X), saw(X) <=> saw(X). 82 | has(X), has(X) <=> has(X). 83 | can_build(X), can_build(X) <=> can_build(X). 84 | can_build_if(X, Y), can_build_if(X, Y) <=> can_build_if(X, Y). 85 | 86 | % common sense 87 | saw(X) ==> has(X). % I saw it, they have it 88 | has(X) ==> can_build(X). % they have it, they can make it 89 | 90 | % this only exists briefly 91 | :- chr_constraint must_have/1. 92 | 93 | % expresses the idea 'they have tanks, they must have a tank factory' 94 | % because needs has a list, we recursively fire the must_have rule 95 | % to add everything 96 | has(X), needs(X, List) ==> must_have(List). 97 | must_have([]) <=> true. 98 | must_have([X|Y]) <=> has(X), must_have(Y). 99 | 100 | % add can_build for everything whose needs exist 101 | % having dock and peasant adds can_build(boat). 102 | % we wait until the first element of list exists, 103 | % then go on to second element and wait, and so on 104 | needs(X, Z) ==> can_build_if(X, Z). 105 | can_build_if(X, []) <=> can_build(X). 106 | has(Y), can_build_if(X, [Y | Z]) <=> can_build_if(X, Z), has(Y). 107 | 108 | % convenience prolog predicate for testing. 109 | % pass list of units seen 110 | i_saw(X) :- 111 | atomic(X), 112 | call(saw(X)), 113 | print_store. 114 | i_saw(X) :- 115 | is_list(X), 116 | maplist(callsaw , X), 117 | print_store. 118 | 119 | callsaw(X) :- call(saw(X)). 120 | 121 | % 122 | % print out results of computation, showing 123 | % what the enemy has built, and what they can build 124 | % 125 | print_store :- 126 | writeln('==============='), 127 | find_chr_constraint(has(Y)), 128 | format('Your enemy has built ~w~n', [Y]), 129 | fail. 130 | print_store :- 131 | nl, 132 | find_chr_constraint(can_build(Y)), 133 | format('Your enemy can build ~w~n', [Y]), 134 | fail. 135 | print_store. 136 | 137 | /******************************* 138 | * helpful utilities * 139 | *******************************/ 140 | 141 | % print out the constraint store 142 | ps :- 143 | find_chr_constraint(Y), 144 | format('constraint store contains ~w~n', [Y]), 145 | fail. 146 | ps. 147 | 148 | % print out constraint store when you return to top level 149 | ss :- set_prolog_flag(chr_toplevel_show_store, true). 150 | 151 | % or don't 152 | noss :- set_prolog_flag(chr_toplevel_show_store, false). 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /final.adoc: -------------------------------------------------------------------------------- 1 | Tutorial - CHR Constraint Handling Rules - Conclusion 2 | ===================================================== 3 | Anne Ogborn 4 | :Author Initials: AO 5 | :toc2: 6 | :icons: 7 | :numbered: 8 | :website: http://www.pathwayslms.com/swipltuts/ 9 | :theme: pathways 10 | 11 | link:/swipltuts/index.html[Up to All Tutorials] 12 | link:index.html[Introduction] 13 | link:basics.html[Basics] 14 | link:examples.html[Examples] 15 | link:constraintsystems.html[Constraint Systems] 16 | link:advanced.html[Advanced] 17 | link:final.html[Final] 18 | 19 | Interesting Uses 20 | ---------------- 21 | 22 | There have been quite a few large systems built with CHR over the years. 23 | 24 | link:https://www.securitease.com/[SecuritEase], often cited as a Prolog user, has various portions of its' system in CHR. Inside SecuritEase CHR 25 | link:https://dtai.cs.kuleuven.be/projects/CHR/papers/draft_chr_survey.pdf[is used to] 26 | : 27 | 28 | 1. implement the logic to recognize advantageous market conditions to auto- 29 | matically place orders in equity markets, 30 | 2. translate high-level queries to SQL, 31 | 3. describe complex relationships between mutually dependent fields on user 32 | input screens, and calculating the consequences of user input actions, and 33 | 4. realize a Financial Information eXchange (FIX) server. 34 | 35 | Some Prolog folks, Falco Nogatz and Christian Hieke, won first place in a 36 | link:http://www1.informatik.uni-wuerzburg.de/en/news/single/news/improving-deutsche-bahn-with-prolog/[Deutsche Bahn Open Data Hackathon] 37 | with a system that analyzed the fares of the German National Railways and discovered several places where it was cheaper to buy a ticket part way, then another ticket on the same train to one's final destination. 38 | 39 | Some Prolog folks enter the large 40 | link:http://ldjam.com[Ludum Dare] game jam when it comes around. The October 2019 entry used CHR to recognize hand drawn houses as part of a game. 41 | 42 | Falco Nogatz has an interesting 43 | link:https://github.com/fnogatz/CHR-Linear-Equation-Solver[linear equation solver] 44 | . 45 | 46 | 47 | Conclusion 48 | ----------- 49 | 50 | CHR is a powerful addition to the logic programmer's toolkit. I hope you'll find it useful. 51 | 52 | I hope you've enjoyed this tutorial. If you notice any mistakes, or want to suggest improvements, or just are totally stumped, email annie (at) swi-prolog (dot) org and let me know. 53 | 54 | You can often find me as Anniepoo on ##prolog channel on freenode.net IRC. 55 | 56 | Massive Thanks 57 | -------------- 58 | 59 | This tutorial, like most of my tutorials, came from my own desire to understand the system. 60 | So I've been somewhere between a constant questioner and an outright pest to many in the CHR 61 | community for the past few months. 62 | 63 | Thanks to Thom Frühwirth for the CHR library and for answering questions on the CHR list and email. 64 | 65 | Alan Baljeu gave much patient coaching on the CHR list, and spent an evening on video call explaining CHR. 66 | 67 | Falco Nogatz provided yet more explanations, as well as many contributions to the CHR ecosystem. 68 | 69 | Tom Schrijvers slides from ICLP are a great resource. I've also stolen a few examples in this tutorial from his work. 70 | 71 | Michael Richter and I puzzled out bits of this together. In particular Michael figured out how the right hand side works. 72 | 73 | Thanks to Gergö Barany for a pleasant afternoon in Vienna spent puzzling out bits of CHR. 74 | 75 | 76 | -------------------------------------------------------------------------------- /house.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/house.png -------------------------------------------------------------------------------- /housebody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/housebody.png -------------------------------------------------------------------------------- /iiCHRpaper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/iiCHRpaper.pdf -------------------------------------------------------------------------------- /images/Robert_Wadlow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/Robert_Wadlow.jpg -------------------------------------------------------------------------------- /images/automaton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/automaton.png -------------------------------------------------------------------------------- /images/automatona.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/automatona.png -------------------------------------------------------------------------------- /images/icons/callouts/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/1.png -------------------------------------------------------------------------------- /images/icons/callouts/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/10.png -------------------------------------------------------------------------------- /images/icons/callouts/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/11.png -------------------------------------------------------------------------------- /images/icons/callouts/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/12.png -------------------------------------------------------------------------------- /images/icons/callouts/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/13.png -------------------------------------------------------------------------------- /images/icons/callouts/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/14.png -------------------------------------------------------------------------------- /images/icons/callouts/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/15.png -------------------------------------------------------------------------------- /images/icons/callouts/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/2.png -------------------------------------------------------------------------------- /images/icons/callouts/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/3.png -------------------------------------------------------------------------------- /images/icons/callouts/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/4.png -------------------------------------------------------------------------------- /images/icons/callouts/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/5.png -------------------------------------------------------------------------------- /images/icons/callouts/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/6.png -------------------------------------------------------------------------------- /images/icons/callouts/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/7.png -------------------------------------------------------------------------------- /images/icons/callouts/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/8.png -------------------------------------------------------------------------------- /images/icons/callouts/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/callouts/9.png -------------------------------------------------------------------------------- /images/icons/caution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/caution.png -------------------------------------------------------------------------------- /images/icons/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/example.png -------------------------------------------------------------------------------- /images/icons/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/home.png -------------------------------------------------------------------------------- /images/icons/important.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/important.png -------------------------------------------------------------------------------- /images/icons/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/next.png -------------------------------------------------------------------------------- /images/icons/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/note.png -------------------------------------------------------------------------------- /images/icons/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/prev.png -------------------------------------------------------------------------------- /images/icons/tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/tip.png -------------------------------------------------------------------------------- /images/icons/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/up.png -------------------------------------------------------------------------------- /images/icons/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anniepoo/swiplchrtut/8f52d1170eb5fbae185ec69527cc564045f238bf/images/icons/warning.png -------------------------------------------------------------------------------- /index.adoc: -------------------------------------------------------------------------------- 1 | Tutorial - CHR Constraint Handling Rules - Intro 2 | ================================================ 3 | Anne Ogborn 4 | :Author Initials: AO 5 | :toc2: 6 | :icons: 7 | :numbered: 8 | :website: http://www.pathwayslms.com/swipltuts/ 9 | :theme: pathways 10 | 11 | 12 | link:/swipltuts/index.html[Up to All Tutorials] 13 | link:index.html[Introduction] 14 | link:basics.html[Basics] 15 | link:examples.html[Examples] 16 | link:constraintsystems.html[Constraint Systems] 17 | link:advanced.html[Advanced] 18 | link:final.html[Final] 19 | 20 | 21 | About This Tutorial 22 | ------------------- 23 | 24 | This tutorial is divided into 5 chapters. 25 | 26 | . Intro 27 | .. Who should do this tutorial? 28 | .. Why another tutorial? 29 | .. What is CHR good for? 30 | .. Intro to CHR 31 | . Basics 32 | .. The constraint store 33 | .. Arguments 34 | .. defining CHR constraints 35 | .. CHR basic syntax 36 | .. Making CHR interact with Prolog 37 | .. Threads 38 | .. Getting 39 | .. Helpful Utilities 40 | . Examples and Patterns 41 | .. A set of worked examples 42 | .. That can be used as exercises 43 | .. And demonstrate common patterns 44 | . Constraint Systems 45 | .. Review 46 | .. A Simple Constraint System 47 | . Advanced material 48 | .. advanced syntax 49 | .. modes 50 | .. types 51 | .. performance 52 | . Conclusion and Final test 53 | 54 | Who Should Do This Tutorial 55 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 56 | 57 | This tutorial is for reasonably experienced SWI-Prolog programmers who want to use Thom Frühwirth's _Constraint_Handling_Rules_ system with **SWI-Prolog**. 58 | 59 | Some parts of the material, notably the chapter on constraint systems, depend on a knowledge of constraint 60 | programming. 61 | 62 | If you want to understand making constraint systems, at least read the intro chapter of my 63 | link:/swipltuts/clpfd/clpfd.html[clp(fd) tutorial] 64 | , the Prolog clpfd library notes, 65 | and the entries for attributed variables in the SWI-Prolog manual. 66 | 67 | The emphasis of this tutorial is *not* on the theory. The author 68 | is not a mathematician, just a long suffering Prolog programmer who 69 | wants to be able to use CHR. 70 | 71 | This tutorial should give you a grasp of the fundamentals of CHR. In particular, you 72 | should come away with a good grasp of how to use CHR for real life tasks. 73 | 74 | Why another tutorial? 75 | ~~~~~~~~~~~~~~~~~~~~~ 76 | 77 | While there is a fair amount of CHR material available, very little of it explains 78 | how CHR interacts with Prolog. I was motivated to write this by the dearth of good 79 | material helping me understand how the two languages interact. 80 | 81 | Additionally, the available examples of CHR tend to be short academic toys. Such are rarely useful 82 | for finding a useful set of patterns for using CHR. 83 | 84 | What Is CHR good for? 85 | ~~~~~~~~~~~~~~~~~~~~~ 86 | 87 | CHR started life as a way to set up constraint systems. This is why the word **constraint** is in the name. 88 | While writing constraint systems is an application, it's not the only one. 89 | 90 | * writing constraint systems 91 | ** When constraint B subsumes constraint A, discard A for better performance 92 | ** When there is a route from power to ground, we have a short 93 | ** Build a constraint system like clp(fd) 94 | ** Build a type system 95 | ** Additionally, constraint systems are often far faster than hand rolled algorithms. 96 | * making programs where things transform into things - 97 | ** A GUI where a button changes appearance when clicked 98 | ** A game where a a spaceship changes appearance after it is hit, fires it's rockets, etc. 99 | ** A simulation like a virtual chemistry lab, where adding A and B to the beaker makes C 100 | ** An interactive fiction game, where the character moves from room to room 101 | ** bottom up recognizers - input characters to words to clauses to sentences to paragraphs. 102 | ** A web application where POST requests modify the store, subject to complex constraints. 103 | * writing recognizers - 104 | ** A traditional bottom up parser 105 | ** A computer vision system that recognizes simple objects like a chimney, a door, etc. and combines them to make 'house'. 106 | ** Expert systems are sometimes mostly recognizers - patient has a cough, fever, trouble swallowing, body aches, they have flu. 107 | * writing expert systems - 108 | ** recognizers in expert systems - motor is making noise and input voltage is normal implies bearing failure 109 | ** resource utilization - I have an extra gate on this IC and need one for this. 110 | ** "design constraint" systems - like the checker tool before submitting a PCB design 111 | * implication 112 | ** An AI player for a game with a 'tech tree', where players must build the **tank_factory** to build tanks. Seeing a **tank** means they have the **tank_factory**. 113 | ** Reasoning about state. A satellite imagery analysis system sees a combine in a field, and reasons the crop is being harvested, so the local grain elevator is full, so a train will come to pick up the grain. 114 | ** When there is a trace on this pcb from a to b, there is an outline of the copper A,B,C,D 115 | ** if an account is overdue and the credit score is poor we will not lend money. 116 | * Search 117 | ** Flexibly turning normalized data that has to be accessed via joins into single lookup data. 118 | * Dynamic type systems. 119 | ** True type systems 120 | ** constraints as a substitute for 'real' type systems. 121 | 122 | What can I come away with? 123 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 124 | 125 | If you study each chapter, pass the quizzes, and do the exercises, you should 126 | be capable of writing CHR at the conclusion. 127 | 128 | How Should I study this? 129 | ~~~~~~~~~~~~~~~~~~~~~~~~ 130 | 131 | Definitely do **not** linearly read the material from start to end without exploring. 132 | In particular, check out the 133 | link:examples.html[Examples] section and refer to it as you go. 134 | 135 | You can ignore the "advanced" material until you've got a solid grounding with the basics. 136 | 137 | Read the material. When examples are presented, try them out in SWI-Prolog. Do the exercises. 138 | When you have questions, try to answer them by trying experiments at the interactor or with a simple 139 | program. That's how I learned this material, largely. 140 | 141 | link:https://github.com/Anniepoo/swiplchrtut[Clone the repo] 142 | for this tutorial. Some of the examples are available in the **examples** directory. 143 | 144 | Seek out other tutorials. 145 | link:https://dtai.cs.kuleuven.be/CHR/files/tutorial_iclp2008.pdf[Tom Schrijvers Slide set from his ICLP presentation] is particularly useful. 146 | 147 | As you start to see what you can do with CHR, develop a goal. Learning is more fun when it's for a specific purpose. 148 | 149 | Once you have a basic grasp, the 150 | link:https://www.swi-prolog.org/pldoc/man?section=debugging[debugger] 151 | can be useful. Placing writelns in bodies can also be useful. CHR prints out it's residual constraints, I suggest printing somethign along with your debug writelns to distinguish yours from CHR's. 152 | 153 | Develop a **cheat sheet** for yourself. When learning, I found it useful to paste this comment, which gives the basic CHR syntax and what is kept and discarded, into my code: 154 | 155 | ---- 156 | /* 157 | * CHR syntax, since I can never remember it: 158 | * name @ retained \ discarded <=> guard | head,body. Simpagation 159 | * name @ discarded <=> guard | head, body. Simplification 160 | * name @ retained ==> guard | head, body. Propagation 161 | * 162 | */ 163 | ---- 164 | 165 | At the bottom of the 166 | link:advanced.html[Advanced] 167 | chapter is a collection of tools. Some may be useful as you learn. 168 | 169 | Don't panic if you don't get it at first. The syntax of CHR is fairly simple, but its a new paradigm and there are lots of small gotchas in the actual use. 170 | 171 | CHR myths 172 | ~~~~~~~~~ 173 | 174 | CHR is not a constraint system, despite the word _constraint_ in the name. The word _constraint_ is in the name because the system manipulates _constraints_ in the sense of _predicates_. 175 | 176 | CHR is a separate language embedded in Prolog. Because CHR constraint rules look like Prolog, and the right hand side (RHS) acts somewhat similar to Prolog, it is tempting to assume they follow the same rules. They do not. Be warned. 177 | 178 | Intro 179 | ----- 180 | 181 | CHR is an embedded language within Prolog. library(chr) is a library included in the standard SWI-Prolog distribution. 182 | 183 | CHR focuses on maintaining a set of _constraints_ in a _constraint store_. You can imagine a container like a box, and blocks with different colors and labels, the 'constraints'. Putting constraints in the store fires off _rules_ that make other constraints appear or disappear from the store and query prolog goals. 184 | 185 | So, in a chemistry simulator, you could add _salt_ and have _salt_ in the store. Then add _tap_water_, and a rule might fire to remove the salt and the water and make _salt_water_. 186 | 187 | Example 188 | ------- 189 | 190 | Here's the code to do the above salt water example. Don't worry if you don't understand it all now. 191 | 192 | ---- 193 | :- use_module(library(chr)). <1> 194 | 195 | :- chr_constraint salt/0, water/0, salt_water/0. <2> 196 | 197 | salt,water <=> salt_water. <3> 198 | 199 | ?- salt, water. <4> 200 | salt_water. <5> 201 | 202 | ?- 203 | ---- 204 | 205 | <1> use_module CHR 206 | <2> define three _chr_constraints_, objects to put in the constraint store. 207 | <3> define a CHR rule - if salt and water are in the store, remove them and put in salt_water. 208 | <4> query that adds salt and water to the store. 209 | <5> CHR prints the contents of the store at the top level by default. The store contains salt_water. 210 | 211 | [EXERCISE] 212 | .Exercise - Punch and Run 213 | ===================================================================== 214 | Run the above salt water example. 215 | 216 | Make tea. Tea is made of water, sugar, and tea_bag. 217 | ===================================================================== 218 | 219 | 220 | Conclusion 221 | ---------- 222 | 223 | With some preliminaries out of the way, we're ready to dive into 224 | link:basics.html[The Basics of CHR] 225 | 226 | -------------------------------------------------------------------------------- /makehtml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | asciidoc -a stylesheet=themes/pathways/style.css -a stylesdir=themes/pathways/ index.adoc >index.html 3 | asciidoc -a stylesheet=themes/pathways/style.css -a stylesdir=themes/pathways/ basics.adoc >basics.html 4 | asciidoc -a stylesheet=themes/pathways/style.css -a stylesdir=themes/pathways/ constraintsystems.adoc >constraintsystems.html 5 | asciidoc -a stylesheet=themes/pathways/style.css -a stylesdir=themes/pathways/ advanced.adoc >advanced.html 6 | asciidoc -a stylesheet=themes/pathways/style.css -a stylesdir=themes/pathways/ final.adoc >final.html 7 | asciidoc -a stylesheet=themes/pathways/style.css -a stylesdir=themes/pathways/ examples.adoc >examples.html 8 | 9 | -------------------------------------------------------------------------------- /moduleb.pl: -------------------------------------------------------------------------------- 1 | :- module(moduleb, [in_b/0]). 2 | 3 | :- use_module(library(chr)). 4 | 5 | :-chr_constraint in_b/0, private_b/0. 6 | 7 | in_b ==> writeln('made in_b'). 8 | 9 | private_b ==> writeln('in private b'). 10 | 11 | 12 | -------------------------------------------------------------------------------- /pathways/pathways.css: -------------------------------------------------------------------------------- 1 | /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ 2 | 3 | /* Default font. */ 4 | body { 5 | font-family: Georgia,serif; 6 | } 7 | 8 | /* Title font. */ 9 | h1, h2, h3, h4, h5, h6, 10 | div.title, caption.title, 11 | thead, p.table.header, 12 | #toctitle, 13 | #author, #revnumber, #revdate, #revremark, 14 | #footer { 15 | font-family: Arial,Helvetica,sans-serif; 16 | } 17 | 18 | body { 19 | margin: 1em 5% 1em 5%; 20 | } 21 | 22 | a { 23 | color: blue; 24 | text-decoration: underline; 25 | } 26 | a:visited { 27 | color: fuchsia; 28 | } 29 | 30 | em { 31 | font-style: italic; 32 | color: navy; 33 | } 34 | 35 | strong { 36 | font-weight: bold; 37 | color: #083194; 38 | } 39 | 40 | h1, h2, h3, h4, h5, h6 { 41 | color: #527bbd; 42 | margin-top: 1.2em; 43 | margin-bottom: 0.5em; 44 | line-height: 1.3; 45 | } 46 | 47 | h1, h2, h3 { 48 | border-bottom: 2px solid silver; 49 | } 50 | h2 { 51 | padding-top: 0.5em; 52 | } 53 | h3 { 54 | float: left; 55 | } 56 | h3 + * { 57 | clear: left; 58 | } 59 | h5 { 60 | font-size: 1.0em; 61 | } 62 | 63 | div.sectionbody { 64 | margin-left: 0; 65 | } 66 | 67 | hr { 68 | border: 1px solid silver; 69 | } 70 | 71 | p { 72 | margin-top: 0.5em; 73 | margin-bottom: 0.5em; 74 | } 75 | 76 | ul, ol, li > p { 77 | margin-top: 0; 78 | } 79 | ul > li { color: #aaa; } 80 | ul > li > * { color: black; } 81 | 82 | pre { 83 | padding: 0; 84 | margin: 0; 85 | } 86 | 87 | #author { 88 | color: #527bbd; 89 | font-weight: bold; 90 | font-size: 1.1em; 91 | } 92 | #email { 93 | } 94 | #revnumber, #revdate, #revremark { 95 | } 96 | 97 | #footer { 98 | font-size: small; 99 | border-top: 2px solid silver; 100 | padding-top: 0.5em; 101 | margin-top: 4.0em; 102 | } 103 | #footer-text { 104 | float: left; 105 | padding-bottom: 0.5em; 106 | } 107 | #footer-badges { 108 | float: right; 109 | padding-bottom: 0.5em; 110 | } 111 | 112 | #preamble { 113 | margin-top: 1.5em; 114 | margin-bottom: 1.5em; 115 | } 116 | div.imageblock, div.exampleblock, div.verseblock, 117 | div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, 118 | div.admonitionblock { 119 | margin-top: 1.0em; 120 | margin-bottom: 1.5em; 121 | } 122 | div.admonitionblock { 123 | margin-top: 2.0em; 124 | margin-bottom: 2.0em; 125 | margin-right: 10%; 126 | color: #606060; 127 | } 128 | 129 | div.content { /* Block element content. */ 130 | padding: 0; 131 | } 132 | 133 | /* Block element titles. */ 134 | div.title, caption.title { 135 | color: #527bbd; 136 | font-weight: bold; 137 | text-align: left; 138 | margin-top: 1.0em; 139 | margin-bottom: 0.5em; 140 | } 141 | div.title + * { 142 | margin-top: 0; 143 | } 144 | 145 | td div.title:first-child { 146 | margin-top: 0.0em; 147 | } 148 | div.content div.title:first-child { 149 | margin-top: 0.0em; 150 | } 151 | div.content + div.title { 152 | margin-top: 0.0em; 153 | } 154 | 155 | div.sidebarblock > div.content { 156 | background: #ffffee; 157 | border: 1px solid #dddddd; 158 | border-left: 4px solid #f0f0f0; 159 | padding: 0.5em; 160 | } 161 | 162 | div.listingblock > div.content { 163 | border: 1px solid #dddddd; 164 | border-left: 5px solid #f0f0f0; 165 | background: #f8f8f8; 166 | padding: 0.5em; 167 | } 168 | 169 | div.quoteblock, div.verseblock { 170 | padding-left: 1.0em; 171 | margin-left: 1.0em; 172 | margin-right: 10%; 173 | border-left: 5px solid #f0f0f0; 174 | color: #777777; 175 | } 176 | 177 | div.quoteblock > div.attribution { 178 | padding-top: 0.5em; 179 | text-align: right; 180 | } 181 | 182 | div.verseblock > pre.content { 183 | font-family: inherit; 184 | font-size: inherit; 185 | } 186 | div.verseblock > div.attribution { 187 | padding-top: 0.75em; 188 | text-align: left; 189 | } 190 | /* DEPRECATED: Pre version 8.2.7 verse style literal block. */ 191 | div.verseblock + div.attribution { 192 | text-align: left; 193 | } 194 | 195 | div.admonitionblock .icon { 196 | vertical-align: top; 197 | font-size: 1.1em; 198 | font-weight: bold; 199 | text-decoration: underline; 200 | color: #527bbd; 201 | padding-right: 0.5em; 202 | } 203 | div.admonitionblock td.content { 204 | padding-left: 0.5em; 205 | border-left: 3px solid #dddddd; 206 | } 207 | 208 | div.exampleblock > div.content { 209 | border-left: 3px solid #dddddd; 210 | padding-left: 0.5em; 211 | } 212 | 213 | div.imageblock div.content { padding-left: 0; } 214 | span.image img { border-style: none; } 215 | a.image:visited { color: white; } 216 | 217 | dl { 218 | margin-top: 0.8em; 219 | margin-bottom: 0.8em; 220 | } 221 | dt { 222 | margin-top: 0.5em; 223 | margin-bottom: 0; 224 | font-style: normal; 225 | color: navy; 226 | } 227 | dd > *:first-child { 228 | margin-top: 0.1em; 229 | } 230 | 231 | ul, ol { 232 | list-style-position: outside; 233 | } 234 | ol.arabic { 235 | list-style-type: decimal; 236 | } 237 | ol.loweralpha { 238 | list-style-type: lower-alpha; 239 | } 240 | ol.upperalpha { 241 | list-style-type: upper-alpha; 242 | } 243 | ol.lowerroman { 244 | list-style-type: lower-roman; 245 | } 246 | ol.upperroman { 247 | list-style-type: upper-roman; 248 | } 249 | 250 | div.compact ul, div.compact ol, 251 | div.compact p, div.compact p, 252 | div.compact div, div.compact div { 253 | margin-top: 0.1em; 254 | margin-bottom: 0.1em; 255 | } 256 | 257 | tfoot { 258 | font-weight: bold; 259 | } 260 | td > div.verse { 261 | white-space: pre; 262 | } 263 | 264 | div.hdlist { 265 | margin-top: 0.8em; 266 | margin-bottom: 0.8em; 267 | } 268 | div.hdlist tr { 269 | padding-bottom: 15px; 270 | } 271 | dt.hdlist1.strong, td.hdlist1.strong { 272 | font-weight: bold; 273 | } 274 | td.hdlist1 { 275 | vertical-align: top; 276 | font-style: normal; 277 | padding-right: 0.8em; 278 | color: navy; 279 | } 280 | td.hdlist2 { 281 | vertical-align: top; 282 | } 283 | div.hdlist.compact tr { 284 | margin: 0; 285 | padding-bottom: 0; 286 | } 287 | 288 | .comment { 289 | background: yellow; 290 | } 291 | 292 | .footnote, .footnoteref { 293 | font-size: 0.8em; 294 | } 295 | 296 | span.footnote, span.footnoteref { 297 | vertical-align: super; 298 | } 299 | 300 | #footnotes { 301 | margin: 20px 0 20px 0; 302 | padding: 7px 0 0 0; 303 | } 304 | 305 | #footnotes div.footnote { 306 | margin: 0 0 5px 0; 307 | } 308 | 309 | #footnotes hr { 310 | border: none; 311 | border-top: 1px solid silver; 312 | height: 1px; 313 | text-align: left; 314 | margin-left: 0; 315 | width: 20%; 316 | min-width: 100px; 317 | } 318 | 319 | div.colist td { 320 | padding-right: 0.5em; 321 | padding-bottom: 0.3em; 322 | vertical-align: top; 323 | } 324 | div.colist td img { 325 | margin-top: 0.3em; 326 | } 327 | 328 | @media print { 329 | #footer-badges { display: none; } 330 | } 331 | 332 | #toc { 333 | margin-bottom: 2.5em; 334 | } 335 | 336 | #toctitle { 337 | color: #527bbd; 338 | font-size: 1.1em; 339 | font-weight: bold; 340 | margin-top: 1.0em; 341 | margin-bottom: 0.1em; 342 | } 343 | 344 | div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { 345 | margin-top: 0; 346 | margin-bottom: 0; 347 | } 348 | div.toclevel2 { 349 | margin-left: 2em; 350 | font-size: 0.9em; 351 | } 352 | div.toclevel3 { 353 | margin-left: 4em; 354 | font-size: 0.9em; 355 | } 356 | div.toclevel4 { 357 | margin-left: 6em; 358 | font-size: 0.9em; 359 | } 360 | 361 | span.aqua { color: aqua; } 362 | span.black { color: black; } 363 | span.blue { color: blue; } 364 | span.fuchsia { color: fuchsia; } 365 | span.gray { color: gray; } 366 | span.green { color: green; } 367 | span.lime { color: lime; } 368 | span.maroon { color: maroon; } 369 | span.navy { color: navy; } 370 | span.olive { color: olive; } 371 | span.purple { color: purple; } 372 | span.red { color: red; } 373 | span.silver { color: silver; } 374 | span.teal { color: teal; } 375 | span.white { color: white; } 376 | span.yellow { color: yellow; } 377 | 378 | span.aqua-background { background: aqua; } 379 | span.black-background { background: black; } 380 | span.blue-background { background: blue; } 381 | span.fuchsia-background { background: fuchsia; } 382 | span.gray-background { background: gray; } 383 | span.green-background { background: green; } 384 | span.lime-background { background: lime; } 385 | span.maroon-background { background: maroon; } 386 | span.navy-background { background: navy; } 387 | span.olive-background { background: olive; } 388 | span.purple-background { background: purple; } 389 | span.red-background { background: red; } 390 | span.silver-background { background: silver; } 391 | span.teal-background { background: teal; } 392 | span.white-background { background: white; } 393 | span.yellow-background { background: yellow; } 394 | 395 | span.big { font-size: 2em; } 396 | span.small { font-size: 0.6em; } 397 | 398 | span.underline { text-decoration: underline; } 399 | span.overline { text-decoration: overline; } 400 | span.line-through { text-decoration: line-through; } 401 | 402 | 403 | /* 404 | * xhtml11 specific 405 | * 406 | * */ 407 | 408 | tt { 409 | font-family: monospace; 410 | font-size: inherit; 411 | color: navy; 412 | } 413 | 414 | div.tableblock { 415 | margin-top: 1.0em; 416 | margin-bottom: 1.5em; 417 | } 418 | div.tableblock > table { 419 | border: 3px solid #527bbd; 420 | } 421 | thead, p.table.header { 422 | font-weight: bold; 423 | color: #527bbd; 424 | } 425 | p.table { 426 | margin-top: 0; 427 | } 428 | /* Because the table frame attribute is overriden by CSS in most browsers. */ 429 | div.tableblock > table[frame="void"] { 430 | border-style: none; 431 | } 432 | div.tableblock > table[frame="hsides"] { 433 | border-left-style: none; 434 | border-right-style: none; 435 | } 436 | div.tableblock > table[frame="vsides"] { 437 | border-top-style: none; 438 | border-bottom-style: none; 439 | } 440 | 441 | 442 | /* 443 | * html5 specific 444 | * 445 | * */ 446 | 447 | .monospaced { 448 | font-family: monospace; 449 | font-size: inherit; 450 | color: navy; 451 | } 452 | 453 | table.tableblock { 454 | margin-top: 1.0em; 455 | margin-bottom: 1.5em; 456 | } 457 | thead, p.tableblock.header { 458 | font-weight: bold; 459 | color: #527bbd; 460 | } 461 | p.tableblock { 462 | margin-top: 0; 463 | } 464 | table.tableblock { 465 | border-width: 3px; 466 | border-spacing: 0px; 467 | border-style: solid; 468 | border-color: #527bbd; 469 | border-collapse: collapse; 470 | } 471 | th.tableblock, td.tableblock { 472 | border-width: 1px; 473 | padding: 4px; 474 | border-style: solid; 475 | border-color: #527bbd; 476 | } 477 | 478 | table.tableblock.frame-topbot { 479 | border-left-style: hidden; 480 | border-right-style: hidden; 481 | } 482 | table.tableblock.frame-sides { 483 | border-top-style: hidden; 484 | border-bottom-style: hidden; 485 | } 486 | table.tableblock.frame-none { 487 | border-style: hidden; 488 | } 489 | 490 | th.tableblock.halign-left, td.tableblock.halign-left { 491 | text-align: left; 492 | } 493 | th.tableblock.halign-center, td.tableblock.halign-center { 494 | text-align: center; 495 | } 496 | th.tableblock.halign-right, td.tableblock.halign-right { 497 | text-align: right; 498 | } 499 | 500 | th.tableblock.valign-top, td.tableblock.valign-top { 501 | vertical-align: top; 502 | } 503 | th.tableblock.valign-middle, td.tableblock.valign-middle { 504 | vertical-align: middle; 505 | } 506 | th.tableblock.valign-bottom, td.tableblock.valign-bottom { 507 | vertical-align: bottom; 508 | } 509 | 510 | 511 | /* 512 | * manpage specific 513 | * 514 | * */ 515 | 516 | body.manpage h1 { 517 | padding-top: 0.5em; 518 | padding-bottom: 0.5em; 519 | border-top: 2px solid silver; 520 | border-bottom: 2px solid silver; 521 | } 522 | body.manpage h2 { 523 | border-style: none; 524 | } 525 | body.manpage div.sectionbody { 526 | margin-left: 3em; 527 | } 528 | 529 | @media print { 530 | body.manpage div#toc { display: none; } 531 | } 532 | 533 | 534 | /* 535 | * Theme specific overrides of the preceding (asciidoc.css) CSS. 536 | * 537 | */ 538 | body { 539 | font-family: Garamond, Georgia, serif; 540 | font-size: 17px; 541 | color: #3E4349; 542 | line-height: 1.3em; 543 | } 544 | h1, h2, h3, h4, h5, h6, 545 | div.title, caption.title, 546 | thead, p.table.header, 547 | #toctitle, 548 | #author, #revnumber, #revdate, #revremark, 549 | #footer { 550 | font-family: Garmond, Georgia, serif; 551 | font-weight: normal; 552 | border-bottom-width: 0; 553 | color: #3E4349; 554 | } 555 | div.title, caption.title { color: #596673; font-weight: bold; } 556 | h1 { font-size: 240%; } 557 | h2 { font-size: 180%; } 558 | h3 { font-size: 150%; } 559 | h4 { font-size: 130%; } 560 | h5 { font-size: 115%; } 561 | h6 { font-size: 100%; } 562 | #header h1 { margin-top: 0; } 563 | #toc { 564 | color: #444444; 565 | line-height: 1.5; 566 | padding-top: 1.5em; 567 | } 568 | #toctitle { 569 | font-size: 20px; 570 | } 571 | #toc a { 572 | border-bottom: 1px dotted #999999; 573 | color: #444444 !important; 574 | text-decoration: none !important; 575 | } 576 | #toc a:hover { 577 | border-bottom: 1px solid #6D4100; 578 | color: #6D4100 !important; 579 | text-decoration: none !important; 580 | } 581 | div.toclevel1 { margin-top: 0.2em; font-size: 16px; } 582 | div.toclevel2 { margin-top: 0.15em; font-size: 14px; } 583 | em, dt, td.hdlist1 { color: black; } 584 | strong { color: #3E4349; } 585 | a { color: #004B6B; text-decoration: none; border-bottom: 1px dotted #004B6B; } 586 | a:visited { color: #615FA0; border-bottom: 1px dotted #615FA0; } 587 | a:hover { color: #6D4100; border-bottom: 1px solid #6D4100; } 588 | div.tableblock > table, table.tableblock { border: 3px solid #E8E8E8; } 589 | th.tableblock, td.tableblock { border: 1px solid #E8E8E8; } 590 | ul > li > * { color: #3E4349; } 591 | pre, tt, .monospaced { font-family: Consolas,Menlo,'Deja Vu Sans Mono','Bitstream Vera Sans Mono',monospace; } 592 | tt, .monospaced { font-size: 0.9em; color: black; 593 | } 594 | div.exampleblock > div.content, div.sidebarblock > div.content, div.listingblock > div.content { border-width: 0 0 0 3px; border-color: #E8E8E8; } 595 | div.verseblock { border-left-width: 0; margin-left: 3em; } 596 | div.quoteblock { border-left-width: 3px; margin-left: 0; margin-right: 0;} 597 | div.admonitionblock td.content { border-left: 3px solid #E8E8E8; } 598 | 599 | 600 | div.imageblock { 601 | float: right; 602 | margin-left: 8px; 603 | } -------------------------------------------------------------------------------- /pathways/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif; 3 | background-color: #EEEEFF; 4 | line-height: 130%; 5 | margin-left: 80px; 6 | margin-right: 120px; 7 | } 8 | 9 | .contents a { 10 | display: block; 11 | line-height: 18pt; 12 | font-variant: small-caps; 13 | text-decoration: none; 14 | color: #CC0000; 15 | } 16 | 17 | .contents a:hover { 18 | text-decoration: underline; 19 | } 20 | 21 | a { 22 | color: #CC0000; 23 | text-decoration: underline; 24 | } 25 | 26 | 27 | 28 | em { 29 | font-weight: bold; 30 | font-style: normal; 31 | } 32 | 33 | code { 34 | white-space: pre-wrap; 35 | } 36 | 37 | pre { 38 | margin: 0; 39 | } 40 | 41 | .precode { 42 | background-color: #dddddd; 43 | margin-left: 24px; 44 | padding: 4px; 45 | } 46 | 47 | .precode.exercise { 48 | background-color: #eeeeee; 49 | border: solid 1px black; 50 | } 51 | 52 | .precode.exercise code{ 53 | font-height: 120%; 54 | } 55 | 56 | .warning { 57 | color: #ff4400; 58 | } 59 | 60 | h1,h2,h3,h4 { 61 | color: #FFFFFF; 62 | background-color: #888888; 63 | } 64 | 65 | h1 { 66 | background-color: #FF0000; 67 | padding: 14px; 68 | } 69 | 70 | h2 { 71 | background-color: #FF4444; 72 | padding: 14px; 73 | } 74 | 75 | h3 { 76 | background-color: #FF8888; 77 | padding: 8px; 78 | } 79 | 80 | h4 { 81 | background-color: #FFCCCC; 82 | color: #444444; 83 | padding: 6px; 84 | } 85 | -------------------------------------------------------------------------------- /pathways/toc2.css: -------------------------------------------------------------------------------- 1 | @media screen { 2 | body { 3 | max-width: 70em; /* approximately 110 characters wide */ 4 | margin-left: 36em; /* 16em visual + 20em for toc - get overloaded */ 5 | } 6 | 7 | #content, #header { 8 | margin-left: 20em; 9 | } 10 | 11 | #toc { 12 | position: fixed; 13 | float: left; 14 | min-height: 100%; 15 | max-width: 20em; 16 | min-width: 20em; 17 | top: 0; 18 | left: 0; 19 | bottom: 0; 20 | width: 13em; 21 | padding: 0.5em; 22 | padding-bottom: 1.5em; 23 | margin: 0; 24 | overflow: auto; 25 | border-right: 3px solid #f8f8f8; 26 | background-color: white; 27 | } 28 | 29 | #toc .toclevel1 { 30 | margin-top: 0.5em; 31 | } 32 | 33 | #toc .toclevel2 { 34 | margin-top: 0.25em; 35 | display: list-item; 36 | color: #aaaaaa; 37 | } 38 | 39 | #toctitle { 40 | margin-top: 0.5em; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sources.txt: -------------------------------------------------------------------------------- 1 | sources 2 | 3 | 4 | https://www.informatik.uni-ulm.de/pm/fileadmin/pm/home/fruehwirth/chr-book-slides-chap1.pdf 5 | 6 | https://www.swi-prolog.org/pldoc/man?section=guidelines 7 | 8 | 9 | https://www.swi-prolog.org/pldoc/man?section=sicstus-chr 10 | 11 | https://zalo.github.io/blog/constraints/ 12 | 13 | https://zalo.github.io/blog/constraints/ 14 | 15 | find_chr_constraint doesn't handle modules. 16 | https://www.swi-prolog.org/pldoc/doc_for?object=chr_runtime%3Acurrent_chr_constraint/1 17 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif; 3 | background-color: #EEEEFF; 4 | line-height: 130%; 5 | margin-left: 80px; 6 | margin-right: 120px; 7 | } 8 | 9 | .contents a { 10 | display: block; 11 | line-height: 18pt; 12 | font-variant: small-caps; 13 | text-decoration: none; 14 | color: #CC0000; 15 | } 16 | 17 | .contents a:hover { 18 | text-decoration: underline; 19 | } 20 | 21 | a { 22 | color: #CC0000; 23 | text-decoration: underline; 24 | } 25 | 26 | 27 | 28 | em { 29 | font-weight: bold; 30 | font-style: normal; 31 | } 32 | 33 | code { 34 | white-space: pre-wrap; 35 | } 36 | 37 | pre { 38 | margin: 0; 39 | } 40 | 41 | .precode { 42 | background-color: #dddddd; 43 | margin-left: 24px; 44 | padding: 4px; 45 | } 46 | 47 | .precode.exercise { 48 | background-color: #eeeeee; 49 | border: solid 1px black; 50 | } 51 | 52 | .precode.exercise code{ 53 | font-height: 120%; 54 | } 55 | 56 | .warning { 57 | color: #ff4400; 58 | } 59 | 60 | h1,h2,h3,h4 { 61 | color: #FFFFFF; 62 | background-color: #888888; 63 | } 64 | 65 | h1 { 66 | background-color: #FF0000; 67 | padding: 14px; 68 | } 69 | 70 | h2 { 71 | background-color: #FF4444; 72 | padding: 14px; 73 | } 74 | 75 | h3 { 76 | background-color: #FF8888; 77 | padding: 8px; 78 | } 79 | 80 | h4 { 81 | background-color: #FFCCCC; 82 | color: #444444; 83 | padding: 6px; 84 | } 85 | -------------------------------------------------------------------------------- /unusedexamples.adoc: -------------------------------------------------------------------------------- 1 | 2 | * Dynamic type systems. 3 | ** True type systems 4 | ** constraints as a substitute for 'real' type systems. 5 | 6 | 7 | <> - go back to the 'things you can build' from intro and explain how to build each one 8 | 9 | 10 | 11 | [Exercise] 12 | .Exercise - radioactive decay 13 | ===================================================================== 14 | We have some isotope `americium_241` which decays to `neptunium_237` and `helium_4`. 15 | 16 | we init some americium into the system, and then time in `tick`s passes. At each `tick` 17 | randomly 10% of the remaining americium decays. 18 | 19 | Simulate this system using CHR. 20 | 21 | ?- americium_241,americium_241,americium_241,americium_241,americium_241,tick,tick,tick. 22 | americium_241, 23 | americium_241, 24 | americium_241, 25 | neptunium_237, 26 | helium_4, 27 | neptunium_237, 28 | helium_4. 29 | 30 | stretch goal - Pick something whose daughter products are unstable and make 31 | a similar simulator for it, decaying the daughters along with the parent. 32 | 33 | ===================================================================== 34 | 35 | 36 | Tech Tree 37 | ~~~~~~~~~ 38 | 39 | This example solves questions about 40 | "tech trees" using Constraint Handling Rules 41 | 42 | Tech Trees are a common game mechanic where the player, 43 | or AI opponent, must build a specific building or unit 44 | before they can build another. 45 | Seeing a unit, the opposing player can then infer that 46 | their opponent possesses all the units needed to 47 | So, for example, if building tanks requires the tank factory, 48 | and the tank factory requires the foundry, then if we see a tank 49 | we can infer they have the foundry. 50 | 51 | Our logic doesn't take into account later destruction of units. 52 | Even if we blow up the tank factory, can we be sure they don't have others? 53 | 54 | ---- 55 | 56 | Pattern - Adjacency 57 | ~~~~~~~~~~~~~~~~~~~ 58 | 59 | when data comes in as an ordered list we can store it in a constraint like 60 | 61 | ---- 62 | data(Prev, Index, Value) 63 | ---- 64 | 65 | and find adjacent values with a rule like 66 | 67 | ---- 68 | data(N, _, V2), data(_, N, V1) ==> ... . 69 | ---- 70 | 71 | [Exercise] 72 | .Exercise - Sawtooth waves 73 | ===================================================================== 74 | Data is provided in a list like this: 75 | [0,1,2,3,5,6,6,7,8,1,2,3,4,6,7,8,3,...] 76 | the data is sawtooths - the value rises gradually, then drops suddenly 77 | 78 | End with a set of constraints drop(N) in the store, recording the points where 79 | the data drops. 80 | 81 | Stretch goal - assume there's a small amount of noise in the data. Only record drops larger than 82 | a threshold passed in. 83 | 84 | Ultra stretch goal - sometimes the DAC will catch a value during the fall 85 | [1,2,3,4,5,6,7,**5**,1,2,3,4,4,5] 86 | 87 | fix your program so it catches these as well. 88 | ===================================================================== 89 | 90 | 91 | 92 | * cellular automaton from schrijvers slides slide 82 93 | * parsing - words 94 | * recipe reasoner using drinks 95 | * ref ld45 96 | * radioactive decay 97 | % given half lives, put in atoms and watch'em decay 98 | % keeping track of time 99 | adventure game. 100 | 'properly dressed' puzzle 101 | Schrijvers 83 - example of 'generate and filter' pattern (56 for generate pattern) 102 | constraint system example. 103 | 104 | Useful patterns 105 | * get_foo 106 | * constraint system 107 | * adjacency 108 | * generate and filter 109 | * collection 110 | * backtracking for labeling 111 | * using order for recognition priority 112 | 113 | More examples 114 | 115 | Reactivation - see Schrijvers slide 96 116 | * domain constraint from slides 96 117 | 118 | 119 | --------------------------------------------------------------------------------