I'm curious about how other Sudoku Solver programmers have implemented their AIC solving routines. Following is a description of what I've done.
I started programming my own Sudoku solver four years ago, and as time and ambition permit, I continue to add various solving techniques. A year ago, I added X chains and XY chains. In the process, I realized that most of the code for X chains and XY chains is identical, and I hate having to duplicate code unnecessarily. So, I abandoned the X and XY chain code, and wrote a generic AIC solver. The basic principle is, I assume, fairly standard: build a table of all strong links, and then build a chain by going through the table and finding which strong links can be joined by means of a weak link. I said "I assume" because when I started writing my solver, I avoided looking at any other solver code. I didn't want to be influenced by how others had done it. But, picking up bits and pieces of various comments made in various forums and sudoku sites, I've concluded that the chain building methods are similar, using recursive tree searching and backtracking to build the chains. This is how I initially implemented my generic AIC solver. However, I ran into problems because the table of strong links is now much larger than what it would be if I was solving only X chains or only XY chains. Processing time grows geometrically with the number of strong links. A typical puzzle, after the initial basics have been solved, can often have tens of thousands of AIC's due to the countless permutations, but the same puzzle might only have 10 or 20 X chains or XY chains. And so, I was finding that my solver was sometimes taking several minutes to generate a list of all AIC's when the number was large. Obviously, for the purpose of solving a puzzle, the solver could stop after finding the first chain, but I wanted a complete list so that I could compare different chains and look for the more interesting ones.
At this point I abandoned the recursive tree search, and adopted an algorithm that I developed several years ago when working on a platform that would not allow recursion. It was essentially a path finding algorithm using an approach that could best be described as cellular automata.
https://en.wikipedia.org/wiki/Cellular_automaton
The table of strong links is the same as before, but with additional weak link data. Every possible weak link for each strong link is also included in the table. The table is now a complete network: essentially a roadmap for getting from point A to point B. In this case A and B are the opposite ends of the AIC. We now have the map, but we still need to find the route. Instead of starting at point A and proceeding with a tree searching algorithm to see where it may lead. We pick both ends and try to have them meet somewhere in the middle. This is where the cellular automata comes into play. In cellular automata, the state of a cell in generation N is determined by its state and its neighbours' states in generation N-1. (In the classic cellular automata Game of Life by John Horton Conway, the cells are arranged on a grid and the neighbours are the cells immediately adjacent.) In the context of finding AIC's, the "cells" are the strong link entries, and the neighbours are the other strong links to which they are weakly linked.
Rather than starting with an arbitrary strong link and seeing where it leads, a target elimination is chosen. For example, suppose we wish to eliminate candidate X from cell A. To do this we need to generate an AIC where both end nodes have a candidate value of X and whose cells can see the target cell A. So, the first step is to search the strong link table to find strong links that meet these criteria. Each one that is suitable is tagged with the information that it is a "terminal node". Each of these terminal nodes is also given a unique ID number.
Now the generations begin. With each new generation, each strong link looks at its immediate neighbours to see if any of them contain a terminal ID number. If so, it knows that this neighbour has a path to the target, and therefore the current node must also be on the path to the target. So, it copies the neighbour's terminal ID and applies it to itself. In this way all terminal nodes will generate paths throughout the network, and spreading in all directions. With each generation the lengths of the partial paths increase by one link. After several generations, one of the strong link nodes may discover that two of its neighbours have paths back to the target, and also that the neighbours' terminal ID's are different. The different terminal ID's mean that the two partial paths lead to different nodes which can each see the target. Therefore, two partial paths have, as we had hoped, met in the middle, and we have a complete AIC with the desired elimination. Because all partial paths are built in parallel, it takes only N/2 generations to find all chains of length N. Furthermore, the first chain to be found is guaranteed to be the shortest one possible. There is no reason to stop after finding the first AIC. We can continue to iterate through generations to see how many different chains can be found. One reason for doing this is that different chains may give additional eliminations due to internal subchains.
Of course, this finds an AIC for only one particular elimination target at a time (except for the subchain eliminations as mentioned above). So, the process has to be repeated for each target elimination. However, the building of the strong link network only has to be done once. After one target elimination is processed, a new set of terminal nodes is generated and the generation process is repeated.
This is how I've implemented my current AIC solver, and I've found it to be considerably faster at finding all possible AIC's than the previous recursive tree searching method. So far, the types of strong links that it uses are: bivalue cells, bilocal digits, UR derived strong links, ALS derived strong links, and group X links. As time permits, and as I think of them, I'll add other strong link types. The program code of the chain solver remains the same; it's just a matter of searching for the different types of strong links during the table building part of the process.
I'm wondering if what I've done here has already been tried by others, or if this is something new.