Day 24: Crossed Wires
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
Haskell part 2, much better solution
Okay, here’s the outline again - this one ran instantly.
Rather than probing with example values, I took a different approach, debugging the structure. I only really care about inputs and outputs, so I wrote something that turns the “wiring diagram” into a map of label -> Expr, where
data Expr = EInput String | EAnd Expr Expr | EOr Expr Expr | EXor Expr Expr deriving (Show, Ord)
(the
Eq
instance is stable in symmatric expressions, eg(==) (EAnd a b) (Eand c d) = a == c && b == d || a == d && b == c
)The expressions are grounded in “inputs” (“x00”…“x44”, “y00”…“y44”) - that is, they just expand out all of the intermediary labelled things.
Then I constructed a circuit that I was after by building a non-swapped 44/45-bit full adder, and produced the same set of expressions for those.
Then: for each output, z00…z45, check the “spec” expression against the actual one. If they’re identical, move on.
Otherwise, find some candidate pairs to swap. For these, I considered all possible labelled outputs except “stable” ones - that is, those that were input depdendencies of z_(i-1) - ie, don’t swap any outputs involved in the computation that’s validated thus far.
searchForSwap :: Exprs -> Layout -> String -> Set.Set String -> [(String, String, Layout, Exprs)] searchForSwap eSpec actual zz stable = let vals = Map.keysSet actual & (`Set.difference` stable) & Set.toList ds = dependencies actual in do k1 <- vals k2 <- vals guard $ k1 < k2 guard $ k1 `Set.notMember` (ds Map.! k2) -- don't create any loops guard $ k2 `Set.notMember` (ds Map.! k1) let actual' = swapPair k1 k2 actual eAct' = exprsForLayout actual' guard $ eSpec Map.! zz == eAct' Map.! zz pure (k1, k2, actual', eAct')
Taking the new layout with swapped outputs and its corresponding set of expressions, carry on searching as before.
A linear scan over the output bits was all that was required - a unique answer poped out without any backtracking.
Anyway, happy Christmas all.
PS. My other version worked (eventually) - it was following this approach that led me to realise that my “spec” full adder was broken too :-D Never skip the unit tests.
(@CameronDev@programming.dev you were asking about alternatives to graphviz-style approaches I recall)
Yes, I was, and this is very impressive. This should be a generic solution right? I’ll have to work out how to run it and test on my input.
Generic-ish. It’ll fit any of the input problems I think. You could fool it by using a non-canonical circuit, because it knows nothing about the equivalence of boolean expressions; and it also relies on one swap sufficing to fix an output, so I didn’t go particularly far into turning it into a generic search. Either of those problem extensions would take much more effort from a solver, so my expectation is that they were deliberately avoided.