From 8f825f68b1458cb066ce1a9851f45bffca2b6a21 Mon Sep 17 00:00:00 2001 From: microproofs Date: Wed, 10 Jan 2024 19:27:21 -0500 Subject: [PATCH 01/13] commit new example for benchmarking --- .../benchmarks/.github/workflows/tests.yml | 20 ++ examples/benchmarks/.gitignore | 6 + examples/benchmarks/README.md | 55 ++++++ examples/benchmarks/aiken.lock | 15 ++ examples/benchmarks/aiken.toml | 14 ++ examples/benchmarks/lib/benchmarks/knights.ak | 71 ++++++++ .../lib/benchmarks/knights/chess_set.ak | 172 ++++++++++++++++++ .../lib/benchmarks/knights/heuristic.ak | 142 +++++++++++++++ .../benchmarks/lib/benchmarks/knights/sort.ak | 3 + .../lib/benchmarks/knights/types.ak | 12 ++ examples/benchmarks/lib/benchmarks/queue.ak | 41 +++++ examples/benchmarks/plutus.json | 14 ++ examples/hello_world/aiken.toml | 2 +- examples/hello_world/plutus.json | 2 +- 14 files changed, 567 insertions(+), 2 deletions(-) create mode 100644 examples/benchmarks/.github/workflows/tests.yml create mode 100644 examples/benchmarks/.gitignore create mode 100644 examples/benchmarks/README.md create mode 100644 examples/benchmarks/aiken.lock create mode 100644 examples/benchmarks/aiken.toml create mode 100644 examples/benchmarks/lib/benchmarks/knights.ak create mode 100644 examples/benchmarks/lib/benchmarks/knights/chess_set.ak create mode 100644 examples/benchmarks/lib/benchmarks/knights/heuristic.ak create mode 100644 examples/benchmarks/lib/benchmarks/knights/sort.ak create mode 100644 examples/benchmarks/lib/benchmarks/knights/types.ak create mode 100644 examples/benchmarks/lib/benchmarks/queue.ak create mode 100644 examples/benchmarks/plutus.json diff --git a/examples/benchmarks/.github/workflows/tests.yml b/examples/benchmarks/.github/workflows/tests.yml new file mode 100644 index 00000000..89136858 --- /dev/null +++ b/examples/benchmarks/.github/workflows/tests.yml @@ -0,0 +1,20 @@ +name: Tests + +on: + push: + branches: ["main"] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: aiken-lang/setup-aiken@v0.1.0 + with: + version: v1 + + - run: aiken fmt --check + - run: aiken check -D + - run: aiken build diff --git a/examples/benchmarks/.gitignore b/examples/benchmarks/.gitignore new file mode 100644 index 00000000..ff7811b1 --- /dev/null +++ b/examples/benchmarks/.gitignore @@ -0,0 +1,6 @@ +# Aiken compilation artifacts +artifacts/ +# Aiken's project working directory +build/ +# Aiken's default documentation export +docs/ diff --git a/examples/benchmarks/README.md b/examples/benchmarks/README.md new file mode 100644 index 00000000..3564414e --- /dev/null +++ b/examples/benchmarks/README.md @@ -0,0 +1,55 @@ +# benchmarks + +Write validators in the `validators` folder, and supporting functions in the `lib` folder using `.ak` as a file extension. + +For example, as `validators/always_true.ak` + +```gleam +validator { + fn spend(_datum: Data, _redeemer: Data, _context: Data) -> Bool { + True + } +} +``` + +## Building + +```sh +aiken build +``` + +## Testing + +You can write tests in any module using the `test` keyword. For example: + +```gleam +test foo() { + 1 + 1 == 2 +} +``` + +To run all tests, simply do: + +```sh +aiken check +``` + +To run only tests matching the string `foo`, do: + +```sh +aiken check -m foo +``` + +## Documentation + +If you're writing a library, you might want to generate an HTML documentation for it. + +Use: + +```sh +aiken docs +``` + +## Resources + +Find more on the [Aiken's user manual](https://aiken-lang.org). diff --git a/examples/benchmarks/aiken.lock b/examples/benchmarks/aiken.lock new file mode 100644 index 00000000..0a72eb3c --- /dev/null +++ b/examples/benchmarks/aiken.lock @@ -0,0 +1,15 @@ +# This file was generated by Aiken +# You typically do not need to edit this file + +[[requirements]] +name = "aiken-lang/stdlib" +version = "1.7.0" +source = "github" + +[[packages]] +name = "aiken-lang/stdlib" +version = "1.7.0" +requirements = [] +source = "github" + +[etags] diff --git a/examples/benchmarks/aiken.toml b/examples/benchmarks/aiken.toml new file mode 100644 index 00000000..7223d60f --- /dev/null +++ b/examples/benchmarks/aiken.toml @@ -0,0 +1,14 @@ +name = "aiken/benchmarks" +version = "0.0.0" +license = "Apache-2.0" +description = "Aiken contracts for project 'aiken/benchmarks'" + +[repository] +user = "aiken" +project = "benchmarks" +platform = "github" + +[[dependencies]] +name = "aiken-lang/stdlib" +version = "1.7.0" +source = "github" diff --git a/examples/benchmarks/lib/benchmarks/knights.ak b/examples/benchmarks/lib/benchmarks/knights.ak new file mode 100644 index 00000000..d99b2ec8 --- /dev/null +++ b/examples/benchmarks/lib/benchmarks/knights.ak @@ -0,0 +1,71 @@ +use aiken/list +use benchmarks/knights/heuristic.{descendants, finished_tour, start_tour} +use benchmarks/knights/types.{ChessSet, Solution} +use benchmarks/queue.{ + Queue, append_all_front, append_front, create_queue, head, is_empty, + remove_front, to_list, +} + +test run_knights0() { + run_knights(0, 0) == [] +} + +test run_knights1() { + run_knights(2, 2) == [] +} + +fn run_knights(depth: Int, board_size: Int) -> Solution { + depth_search(depth, root(board_size), grow, is_fin) |> to_list +} + +fn depth_search( + depth: Int, + queue: Queue, + grow_fn: fn(a) -> List, + fin_fn: fn(a) -> Bool, +) -> Queue { + if depth == 0 || is_empty(queue) { + create_queue() + } else if fin_fn(head(queue)) { + depth_search(depth - 1, remove_front(queue), grow_fn, fin_fn) + |> append_front(head(queue)) + } else { + append_all_front(remove_front(queue), grow_fn(head(queue))) + |> depth_search(depth - 1, _, grow_fn, fin_fn) + } +} + +fn root(sze: Int) -> Queue<(Int, ChessSet)> { + append_all_front(create_queue(), mk_starts(sze)) +} + +fn mk_starts(sze: Int) -> List<(Int, ChessSet)> { + let x_list = interval(1, sze) + let y_list = interval(1, sze) + + let l = x_list |> list.map2(y_list, fn(a, b) { start_tour((a, b), sze) }) + let length = list.length(l) + + list.repeat(1 - length, length) |> list.zip(l) +} + +fn interval(a: Int, b: Int) -> List { + if a > b { + [] + } else { + [a, ..interval(a + 1, b)] + } +} + +fn grow(item: (Int, ChessSet)) -> List<(Int, ChessSet)> { + let (x, y) = item + + let const_item = x + 1 + descendants(y) |> list.map(fn(list_item) { (const_item, list_item) }) +} + +fn is_fin(item: (Int, ChessSet)) -> Bool { + let (_, y) = item + + finished_tour(y) +} diff --git a/examples/benchmarks/lib/benchmarks/knights/chess_set.ak b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak new file mode 100644 index 00000000..4ff445e4 --- /dev/null +++ b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak @@ -0,0 +1,172 @@ +use aiken/builtin +use aiken/list +use benchmarks/knights/types.{ChessSet, Tile} + +pub fn create_board(size: Int, init_square: Tile) -> ChessSet { + ChessSet { + size, + move_number: 1, + start: Some(init_square), + visited: [init_square], + } +} + +pub fn add_piece(board: ChessSet, tile: Tile) -> ChessSet { + // record update + ChessSet { + ..board, + move_number: board.move_number + 1, + visited: [tile, ..board.visited], + } +} + +pub fn first_piece(board: ChessSet) -> Tile { + expect Some(tile) = board.start + tile +} + +pub fn delete_first(board: ChessSet) -> ChessSet { + let visited = board.visited + + expect Some(deleted_first) = list.init(visited) + + ChessSet { ..board, start: second_last(visited), visited: deleted_first } +} + +pub fn second_last(visited: List) -> Option { + when visited is { + [] -> None + [_, ..rest] -> { + let value = second_last(rest) + + if value == None { + if builtin.null_list(rest) { + None + } else { + Some(builtin.head_list(visited)) + } + } else { + value + } + } + } +} +// {-# INLINABLE createBoard #-} +// createBoard :: Integer -> Tile -> ChessSet +// createBoard x t = Board x 1 (Just t) [t] + +// {-# INLINABLE sizeBoard #-} +// sizeBoard :: ChessSet -> Integer +// sizeBoard (Board s _ _ _) = s + +// {-# INLINABLE noPieces #-} +// noPieces :: ChessSet -> Integer +// noPieces (Board _ n _ _) = n + +// {-# INLINABLE addPiece #-} +// addPiece :: Tile -> ChessSet -> ChessSet +// addPiece t (Board s n f ts) = Board s (n+1) f (t:ts) + +// -- % Remove the last element from a list +// {-# INLINABLE init #-} +// init :: [a] -> [a] +// init l = case reverse l of +// _:as -> reverse as +// [] -> Tx.error () + +// {-# INLINABLE secondLast #-} +// secondLast :: [a] -> Maybe a +// secondLast l = +// case reverse l of +// [] -> Tx.error () +// [_] -> Nothing +// _:a:_ -> Just a + +// {-% Note [deleteFirst]. +// deleteFirst removes the first position from the tour. +// Since the sequence of positions (ts) is stored in reverse this involves +// deleting the last element of ts and also storing the second-last element of +// ts as the new starting position. In the strict world this will *fail* if the +// length of ts is 1. The lazy version got away with this because the starting +// position is never examined in that case (possibly just through luck: with +// enough backtracking that might still happen). To solve this we have to store +// the starting position as a Maybe value, deferring any error until we actually +// look at it. +// %-} + +// {-# INLINABLE deleteFirst #-} +// deleteFirst :: ChessSet -> ChessSet +// deleteFirst (Board s n _ ts) = +// Board s (n-1) f' ts' +// where ts' = init ts +// f' = secondLast ts + +// {-# INLINABLE positionPiece #-} +// positionPiece :: Integer -> ChessSet -> Tile +// positionPiece x (Board _ n _ ts) = ts Tx.!! (n - x) + +// {-# INLINABLE lastPiece #-} +// lastPiece :: ChessSet -> Tile +// lastPiece (Board _ _ _ (t:_)) = t +// lastPiece _ = Tx.error () + +// {-# INLINABLE firstPiece #-} +// firstPiece :: ChessSet -> Tile +// firstPiece (Board _ _ f _) = +// case f of Just tile -> tile +// Nothing -> Tx.error () + +// {-# INLINABLE pieceAtTile #-} +// pieceAtTile :: Tile -> ChessSet -> Integer +// pieceAtTile x0 (Board _ _ _ ts) +// = findPiece x0 ts +// where +// findPiece _ [] = Tx.error () +// findPiece x (y:xs) +// | x == y = 1 + Tx.length xs +// | otherwise = findPiece x xs + +// {-# INLINABLE notIn #-} +// notIn :: Eq a => a -> [a] -> Bool +// notIn _ [] = True +// notIn x (a:as) = (x /= a) && (notIn x as) + +// {-# INLINABLE isSquareFree #-} +// isSquareFree :: Tile -> ChessSet -> Bool +// isSquareFree x (Board _ _ _ ts) = notIn x ts + +// -- % Everything below here is only needed for printing boards. +// -- % This is useful for debugging. + +// instance Haskell.Show ChessSet where +// showsPrec _ (Board sze n _ ts) +// = Haskell.showString (printBoard sze sortedTrail 1) +// where sortedTrail = quickSort (assignMoveNo ts sze n) + +// assignMoveNo :: [Tile] -> Integer -> Integer -> [Tile] +// assignMoveNo [] _ _ +// = [] +// assignMoveNo ((x,y):t) size z +// = (((y-1)*size)+x,z):assignMoveNo t size (z-1) + +// printBoard :: Integer -> [Tile] -> Integer -> Haskell.String +// printBoard s [] n +// | (n > (s*s)) = "" +// | ((n `Haskell.mod` s) /=0)= "*"++(spaces (s*s) 1) ++(printBoard s [] (n+1)) +// | ((n `Haskell.mod` s) ==0)= "*\n" ++(printBoard s [] (n+1)) +// printBoard s trail@((i,j):xs) n +// | (i==n) && +// ((n `Haskell.mod` s) ==0) = (Haskell.show j)++"\n"++(printBoard s xs (n+1)) +// | (i==n) && +// ((n `Haskell.mod` s) /=0)= (Haskell.show j)++(spaces (s*s) j)++(printBoard s xs (n+1)) +// | ((n `Haskell.mod` s) /=0)= "*" ++(spaces (s*s) 1)++(printBoard s trail (n+1)) +// | ((n `Haskell.mod` s) ==0)= "*\n" ++(printBoard s trail (n+1)) +// printBoard _ _ _ = "?" + +// spaces :: Integer -> Integer -> Haskell.String +// spaces s y = +// take' ((logTen s) - (logTen y) + 1) [' ',' '..] +// where +// logTen :: Integer -> Integer +// logTen 0 = 0 +// logTen x = 1 + logTen (x `Haskell.div` 10) diff --git a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak new file mode 100644 index 00000000..f736cd32 --- /dev/null +++ b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak @@ -0,0 +1,142 @@ +use aiken/builtin +use aiken/list +use benchmarks/knights/chess_set.{add_piece, create_board, first_piece} +use benchmarks/knights/sort.{quick_sort} +use benchmarks/knights/types.{ChessSet, Tile} + +pub fn start_tour(st: Tile, size: Int) -> ChessSet { + expect 0 = builtin.remainder_integer(size, 2) + + create_board(size, st) +} + +pub fn finished_tour(board: ChessSet) -> Bool { + let ChessSet { move_number, size, .. } = board + + move_number == size * size && can_jump_first(board) +} + +pub fn descendants(board: ChessSet) -> List { + if and { + can_jump_first(board), + board + |> add_piece(first_piece(board)) + |> dead_end, + } { + [] + } else { + let singles = single_descend(board) + + when singles is { + [] -> board |> desc_and_no |> quick_sort |> list.map(builtin.snd_pair) + [_] -> singles + _ -> + [] + } + } +} + +pub fn can_jump_first(board: ChessSet) -> Bool { + todo +} + +pub fn dead_end(board: ChessSet) -> Bool { + todo +} + +pub fn single_descend(board: ChessSet) -> List { + todo +} + +pub fn desc_and_no(board: ChessSet) -> List<(Int, ChessSet)> { + todo +} + +pub fn can_move_to(board: ChessSet, tile: Tile) -> Bool { + todo +} +// data Direction = UL | UR | DL |DR | LU | LD | RU | RD + +// {-# INLINABLE move #-} +// move :: Direction -> Tile -> Tile +// move UL (x,y) = (x-1,y-2) +// move UR (x,y) = (x+1,y-2) +// move DL (x,y) = (x-1,y+2) +// move DR (x,y) = (x+1,y+2) +// move LU (x,y) = (x-2,y-1) +// move LD (x,y) = (x-2,y+1) +// move RU (x,y) = (x+2,y-1) +// move RD (x,y) = (x+2,y+1) + +// {-# INLINABLE startTour #-} +// startTour :: Tile -> Integer -> ChessSet +// startTour st size +// | (size `Tx.remainder` 2) == 0 = createBoard size st +// | otherwise = {-Tx.trace "startTour" $ -} Tx.error () + +// {-# INLINABLE moveKnight #-} +// moveKnight :: ChessSet -> Direction -> ChessSet +// moveKnight board dir +// = addPiece (move dir (lastPiece board)) board + +// {-# INLINABLE canMove #-} +// canMove :: ChessSet -> Direction -> Bool +// canMove board dir +// = canMoveTo (move dir (lastPiece board)) board + +// {-# INLINABLE canMoveTo #-} +// canMoveTo :: Tile -> ChessSet -> Bool +// canMoveTo t@(x,y) board +// = (x Tx.>= 1) && (x Tx.<= sze) && +// (y Tx.>= 1) && (y Tx.<= sze) && +// isSquareFree t board +// where +// sze = sizeBoard board + +// {-# INLINABLE descendents #-} +// descendents :: ChessSet -> [ChessSet] +// descendents board = +// if (canJumpFirst board) && (deadEnd (addPiece (firstPiece board) board)) +// then [] +// else +// let l = Tx.length singles in +// if l == 0 then map snd (quickSort (descAndNo board)) +// else if l == 1 then singles +// else [] -- Going to be dead end +// where +// singles = singleDescend board + +// {-# INLINABLE singleDescend #-} +// singleDescend :: ChessSet -> [ChessSet] +// singleDescend board =[x | (y,x) <- descAndNo board, y==1] + +// {-# INLINABLE descAndNo #-} +// descAndNo :: ChessSet -> [(Integer,ChessSet)] +// descAndNo board +// = [(Tx.length (possibleMoves (deleteFirst x)),x) | x <- allDescend board] + +// {-# INLINABLE allDescend #-} +// allDescend :: ChessSet -> [ChessSet] +// allDescend board +// = map (moveKnight board) (possibleMoves board) + +// {-# INLINABLE possibleMoves #-} +// possibleMoves :: ChessSet -> [Direction] +// possibleMoves board +// =[x | x <- [UL,UR,DL,DR,LU,LD,RU,RD], (canMove board x)] + +// {-# INLINABLE deadEnd #-} +// deadEnd :: ChessSet -> Bool +// deadEnd board = (Tx.length (possibleMoves board)) == 0 + +// {-# INLINABLE canJumpFirst #-} +// canJumpFirst :: ChessSet -> Bool +// canJumpFirst board +// = canMoveTo (firstPiece board) (deleteFirst board) + +// {-# INLINABLE tourFinished #-} +// tourFinished :: ChessSet -> Bool +// tourFinished board +// = (noPieces board == (sze*sze)) && (canJumpFirst board) +// where +// sze = sizeBoard board diff --git a/examples/benchmarks/lib/benchmarks/knights/sort.ak b/examples/benchmarks/lib/benchmarks/knights/sort.ak new file mode 100644 index 00000000..6e429da9 --- /dev/null +++ b/examples/benchmarks/lib/benchmarks/knights/sort.ak @@ -0,0 +1,3 @@ +pub fn quick_sort(l: List) -> List { + todo +} diff --git a/examples/benchmarks/lib/benchmarks/knights/types.ak b/examples/benchmarks/lib/benchmarks/knights/types.ak new file mode 100644 index 00000000..75449ffb --- /dev/null +++ b/examples/benchmarks/lib/benchmarks/knights/types.ak @@ -0,0 +1,12 @@ +pub type Tile = + (Int, Int) + +pub type ChessSet { + move_number: Int, + visited: List, + size: Int, + start: Option, +} + +pub type Solution = + List<(Int, ChessSet)> diff --git a/examples/benchmarks/lib/benchmarks/queue.ak b/examples/benchmarks/lib/benchmarks/queue.ak new file mode 100644 index 00000000..9a5fbb06 --- /dev/null +++ b/examples/benchmarks/lib/benchmarks/queue.ak @@ -0,0 +1,41 @@ +use aiken/list + +pub opaque type Queue { + inner: List, +} + +pub fn create_queue() -> Queue { + [] |> Queue +} + +pub fn to_list(queue: Queue) -> List { + queue.inner +} + +pub fn is_empty(queue: Queue) -> Bool { + when queue.inner is { + [] -> True + _ -> False + } +} + +pub fn append_front(queue: Queue, item: a) -> Queue { + list.push(queue.inner, item) |> Queue +} + +/// Add all items from the list to the front of the queue +pub fn append_all_front(queue: Queue, items: List) -> Queue { + list.concat(items, queue.inner) |> Queue +} + +pub fn remove_front(queue: Queue) -> Queue { + expect [_, ..rest] = queue.inner + + rest |> Queue +} + +pub fn head(queue: Queue) -> a { + expect [q, ..] = queue.inner + + q +} diff --git a/examples/benchmarks/plutus.json b/examples/benchmarks/plutus.json new file mode 100644 index 00000000..5c4319e1 --- /dev/null +++ b/examples/benchmarks/plutus.json @@ -0,0 +1,14 @@ +{ + "preamble": { + "title": "aiken/benchmarks", + "description": "Aiken contracts for project 'aiken/benchmarks'", + "version": "0.0.0", + "plutusVersion": "v2", + "compiler": { + "name": "Aiken", + "version": "v1.0.21-alpha+4b04517" + }, + "license": "Apache-2.0" + }, + "validators": [] +} \ No newline at end of file diff --git a/examples/hello_world/aiken.toml b/examples/hello_world/aiken.toml index 3b523f22..1e530f11 100644 --- a/examples/hello_world/aiken.toml +++ b/examples/hello_world/aiken.toml @@ -5,5 +5,5 @@ description = "Aiken contracts for project 'aiken-lang/hello_world'" [[dependencies]] name = "aiken-lang/stdlib" -version = "main" +version = "1.7.0" source = "github" diff --git a/examples/hello_world/plutus.json b/examples/hello_world/plutus.json index a0fab843..5b6e3cc0 100644 --- a/examples/hello_world/plutus.json +++ b/examples/hello_world/plutus.json @@ -6,7 +6,7 @@ "plutusVersion": "v2", "compiler": { "name": "Aiken", - "version": "v1.0.19-alpha+d56d518" + "version": "v1.0.21-alpha+4b04517" } }, "validators": [ From c62821e02a13691aeb03e7bfa77a9fe2f162c2d6 Mon Sep 17 00:00:00 2001 From: microproofs Date: Thu, 11 Jan 2024 14:44:51 -0500 Subject: [PATCH 02/13] continue porting over knights example --- .../lib/benchmarks/knights/chess_set.ak | 19 ++++- .../lib/benchmarks/knights/heuristic.ak | 81 +++++++++++++++++-- .../lib/benchmarks/knights/types.ak | 2 +- 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/examples/benchmarks/lib/benchmarks/knights/chess_set.ak b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak index 4ff445e4..465758e7 100644 --- a/examples/benchmarks/lib/benchmarks/knights/chess_set.ak +++ b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak @@ -26,14 +26,19 @@ pub fn first_piece(board: ChessSet) -> Tile { } pub fn delete_first(board: ChessSet) -> ChessSet { - let visited = board.visited + let ChessSet { move_number, visited, .. } = board expect Some(deleted_first) = list.init(visited) - ChessSet { ..board, start: second_last(visited), visited: deleted_first } + ChessSet { + ..board, + move_number: move_number - 1, + start: second_last(visited), + visited: deleted_first, + } } -pub fn second_last(visited: List) -> Option { +fn second_last(visited: List) -> Option { when visited is { [] -> None [_, ..rest] -> { @@ -51,6 +56,14 @@ pub fn second_last(visited: List) -> Option { } } } + +pub fn is_square_free(board: ChessSet, tile: Tile) -> Bool { + not_in(board.visited, tile) +} + +pub fn not_in(tiles: List, tile: a) -> Bool { + !list.has(tiles, tile) +} // {-# INLINABLE createBoard #-} // createBoard :: Integer -> Tile -> ChessSet // createBoard x t = Board x 1 (Just t) [t] diff --git a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak index f736cd32..37d27f22 100644 --- a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak +++ b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak @@ -1,9 +1,37 @@ use aiken/builtin use aiken/list -use benchmarks/knights/chess_set.{add_piece, create_board, first_piece} +use benchmarks/knights/chess_set.{ + add_piece, create_board, delete_first, first_piece, is_square_free, +} use benchmarks/knights/sort.{quick_sort} use benchmarks/knights/types.{ChessSet, Tile} +type Direction { + UL + UR + DL + DR + LU + LD + RU + RD +} + +fn move(direction: Direction, tile: Tile) -> Tile { + let (x, y) = tile + + when direction is { + UL -> (x - 1, y - 2) + UR -> (x + 1, y - 2) + DL -> (x - 1, y + 2) + DR -> (x + 1, y + 2) + LU -> (x - 2, y - 1) + LD -> (x - 2, y + 1) + RU -> (x + 2, y - 1) + RD -> (x + 2, y + 1) + } +} + pub fn start_tour(st: Tile, size: Int) -> ChessSet { expect 0 = builtin.remainder_integer(size, 2) @@ -20,7 +48,8 @@ pub fn descendants(board: ChessSet) -> List { if and { can_jump_first(board), board - |> add_piece(first_piece(board)) + |> first_piece + |> add_piece(board, _) |> dead_end, } { [] @@ -37,22 +66,62 @@ pub fn descendants(board: ChessSet) -> List { } pub fn can_jump_first(board: ChessSet) -> Bool { - todo + can_move_to(delete_first(board), first_piece(board)) } pub fn dead_end(board: ChessSet) -> Bool { - todo + board |> possible_moves |> builtin.null_list } pub fn single_descend(board: ChessSet) -> List { - todo + let descendants_and_moves = desc_and_no(board) + list.filter_map( + descendants_and_moves, + fn(item) { + let (moves, board) = item + if moves == 1 { + Some(board) + } else { + None + } + }, + ) } pub fn desc_and_no(board: ChessSet) -> List<(Int, ChessSet)> { - todo + board + |> all_descend + |> list.map( + fn(item) { + (item |> delete_first |> possible_moves |> list.length, item) + }, + ) } pub fn can_move_to(board: ChessSet, tile: Tile) -> Bool { + let (x, y) = tile + let size = board.size + + and { + x >= 1, + x <= size, + y >= 1, + y <= size, + is_square_free(board, tile), + } +} + +pub fn all_descend(board: ChessSet) -> List { + board + |> possible_moves + |> list.map(fn(direction) { move_knight(board, direction) }) +} + +fn move_knight(board: ChessSet, direction: Direction) -> ChessSet { + todo +} + +fn possible_moves(board: ChessSet) -> List { todo } // data Direction = UL | UR | DL |DR | LU | LD | RU | RD diff --git a/examples/benchmarks/lib/benchmarks/knights/types.ak b/examples/benchmarks/lib/benchmarks/knights/types.ak index 75449ffb..fc293456 100644 --- a/examples/benchmarks/lib/benchmarks/knights/types.ak +++ b/examples/benchmarks/lib/benchmarks/knights/types.ak @@ -2,9 +2,9 @@ pub type Tile = (Int, Int) pub type ChessSet { + size: Int, move_number: Int, visited: List, - size: Int, start: Option, } From 1d16cbf3862b89521dadee40cdd3a9250b6f87cf Mon Sep 17 00:00:00 2001 From: microproofs Date: Sat, 13 Jan 2024 13:21:46 -0500 Subject: [PATCH 03/13] more benchmark work --- examples/benchmarks/lib/benchmarks/knights.ak | 4 +- .../lib/benchmarks/knights/chess_set.ak | 82 +++------------ .../lib/benchmarks/knights/heuristic.ak | 99 +++---------------- 3 files changed, 25 insertions(+), 160 deletions(-) diff --git a/examples/benchmarks/lib/benchmarks/knights.ak b/examples/benchmarks/lib/benchmarks/knights.ak index d99b2ec8..f6d01146 100644 --- a/examples/benchmarks/lib/benchmarks/knights.ak +++ b/examples/benchmarks/lib/benchmarks/knights.ak @@ -7,11 +7,11 @@ use benchmarks/queue.{ } test run_knights0() { - run_knights(0, 0) == [] + run_knights(100, 0) == [] } test run_knights1() { - run_knights(2, 2) == [] + run_knights(100, 2) == [] } fn run_knights(depth: Int, board_size: Int) -> Solution { diff --git a/examples/benchmarks/lib/benchmarks/knights/chess_set.ak b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak index 465758e7..8d9454b1 100644 --- a/examples/benchmarks/lib/benchmarks/knights/chess_set.ak +++ b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak @@ -25,6 +25,18 @@ pub fn first_piece(board: ChessSet) -> Tile { tile } +// {-# INLINABLE lastPiece #-} +// lastPiece :: ChessSet -> Tile +// lastPiece (Board _ _ _ (t:_)) = t +// lastPiece _ = Tx.error () + +pub fn last_piece(board: ChessSet) -> Tile { + when board.visited is { + [] -> fail + [x, ..] -> x + } +} + pub fn delete_first(board: ChessSet) -> ChessSet { let ChessSet { move_number, visited, .. } = board @@ -64,71 +76,10 @@ pub fn is_square_free(board: ChessSet, tile: Tile) -> Bool { pub fn not_in(tiles: List, tile: a) -> Bool { !list.has(tiles, tile) } -// {-# INLINABLE createBoard #-} -// createBoard :: Integer -> Tile -> ChessSet -// createBoard x t = Board x 1 (Just t) [t] - -// {-# INLINABLE sizeBoard #-} -// sizeBoard :: ChessSet -> Integer -// sizeBoard (Board s _ _ _) = s - -// {-# INLINABLE noPieces #-} -// noPieces :: ChessSet -> Integer -// noPieces (Board _ n _ _) = n - -// {-# INLINABLE addPiece #-} -// addPiece :: Tile -> ChessSet -> ChessSet -// addPiece t (Board s n f ts) = Board s (n+1) f (t:ts) - -// -- % Remove the last element from a list -// {-# INLINABLE init #-} -// init :: [a] -> [a] -// init l = case reverse l of -// _:as -> reverse as -// [] -> Tx.error () - -// {-# INLINABLE secondLast #-} -// secondLast :: [a] -> Maybe a -// secondLast l = -// case reverse l of -// [] -> Tx.error () -// [_] -> Nothing -// _:a:_ -> Just a - -// {-% Note [deleteFirst]. -// deleteFirst removes the first position from the tour. -// Since the sequence of positions (ts) is stored in reverse this involves -// deleting the last element of ts and also storing the second-last element of -// ts as the new starting position. In the strict world this will *fail* if the -// length of ts is 1. The lazy version got away with this because the starting -// position is never examined in that case (possibly just through luck: with -// enough backtracking that might still happen). To solve this we have to store -// the starting position as a Maybe value, deferring any error until we actually -// look at it. -// %-} - -// {-# INLINABLE deleteFirst #-} -// deleteFirst :: ChessSet -> ChessSet -// deleteFirst (Board s n _ ts) = -// Board s (n-1) f' ts' -// where ts' = init ts -// f' = secondLast ts - // {-# INLINABLE positionPiece #-} // positionPiece :: Integer -> ChessSet -> Tile // positionPiece x (Board _ n _ ts) = ts Tx.!! (n - x) -// {-# INLINABLE lastPiece #-} -// lastPiece :: ChessSet -> Tile -// lastPiece (Board _ _ _ (t:_)) = t -// lastPiece _ = Tx.error () - -// {-# INLINABLE firstPiece #-} -// firstPiece :: ChessSet -> Tile -// firstPiece (Board _ _ f _) = -// case f of Just tile -> tile -// Nothing -> Tx.error () - // {-# INLINABLE pieceAtTile #-} // pieceAtTile :: Tile -> ChessSet -> Integer // pieceAtTile x0 (Board _ _ _ ts) @@ -139,15 +90,6 @@ pub fn not_in(tiles: List, tile: a) -> Bool { // | x == y = 1 + Tx.length xs // | otherwise = findPiece x xs -// {-# INLINABLE notIn #-} -// notIn :: Eq a => a -> [a] -> Bool -// notIn _ [] = True -// notIn x (a:as) = (x /= a) && (notIn x as) - -// {-# INLINABLE isSquareFree #-} -// isSquareFree :: Tile -> ChessSet -> Bool -// isSquareFree x (Board _ _ _ ts) = notIn x ts - // -- % Everything below here is only needed for printing boards. // -- % This is useful for debugging. diff --git a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak index 37d27f22..9099b24a 100644 --- a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak +++ b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak @@ -1,7 +1,7 @@ use aiken/builtin use aiken/list use benchmarks/knights/chess_set.{ - add_piece, create_board, delete_first, first_piece, is_square_free, + add_piece, create_board, delete_first, first_piece, is_square_free, last_piece, } use benchmarks/knights/sort.{quick_sort} use benchmarks/knights/types.{ChessSet, Tile} @@ -17,6 +17,10 @@ type Direction { RD } +fn direction_list() { + [UL, UR, DL, DR, LU, LD, RU, RD] +} + fn move(direction: Direction, tile: Tile) -> Tile { let (x, y) = tile @@ -111,6 +115,10 @@ pub fn can_move_to(board: ChessSet, tile: Tile) -> Bool { } } +fn can_move(board: ChessSet, direction: Direction) -> Bool { + board |> can_move_to(move(direction, last_piece(board))) +} + pub fn all_descend(board: ChessSet) -> List { board |> possible_moves @@ -118,94 +126,9 @@ pub fn all_descend(board: ChessSet) -> List { } fn move_knight(board: ChessSet, direction: Direction) -> ChessSet { - todo + add_piece(board, move(direction, last_piece(board))) } fn possible_moves(board: ChessSet) -> List { - todo + direction_list() |> list.filter(fn(direction) { can_move(board, direction) }) } -// data Direction = UL | UR | DL |DR | LU | LD | RU | RD - -// {-# INLINABLE move #-} -// move :: Direction -> Tile -> Tile -// move UL (x,y) = (x-1,y-2) -// move UR (x,y) = (x+1,y-2) -// move DL (x,y) = (x-1,y+2) -// move DR (x,y) = (x+1,y+2) -// move LU (x,y) = (x-2,y-1) -// move LD (x,y) = (x-2,y+1) -// move RU (x,y) = (x+2,y-1) -// move RD (x,y) = (x+2,y+1) - -// {-# INLINABLE startTour #-} -// startTour :: Tile -> Integer -> ChessSet -// startTour st size -// | (size `Tx.remainder` 2) == 0 = createBoard size st -// | otherwise = {-Tx.trace "startTour" $ -} Tx.error () - -// {-# INLINABLE moveKnight #-} -// moveKnight :: ChessSet -> Direction -> ChessSet -// moveKnight board dir -// = addPiece (move dir (lastPiece board)) board - -// {-# INLINABLE canMove #-} -// canMove :: ChessSet -> Direction -> Bool -// canMove board dir -// = canMoveTo (move dir (lastPiece board)) board - -// {-# INLINABLE canMoveTo #-} -// canMoveTo :: Tile -> ChessSet -> Bool -// canMoveTo t@(x,y) board -// = (x Tx.>= 1) && (x Tx.<= sze) && -// (y Tx.>= 1) && (y Tx.<= sze) && -// isSquareFree t board -// where -// sze = sizeBoard board - -// {-# INLINABLE descendents #-} -// descendents :: ChessSet -> [ChessSet] -// descendents board = -// if (canJumpFirst board) && (deadEnd (addPiece (firstPiece board) board)) -// then [] -// else -// let l = Tx.length singles in -// if l == 0 then map snd (quickSort (descAndNo board)) -// else if l == 1 then singles -// else [] -- Going to be dead end -// where -// singles = singleDescend board - -// {-# INLINABLE singleDescend #-} -// singleDescend :: ChessSet -> [ChessSet] -// singleDescend board =[x | (y,x) <- descAndNo board, y==1] - -// {-# INLINABLE descAndNo #-} -// descAndNo :: ChessSet -> [(Integer,ChessSet)] -// descAndNo board -// = [(Tx.length (possibleMoves (deleteFirst x)),x) | x <- allDescend board] - -// {-# INLINABLE allDescend #-} -// allDescend :: ChessSet -> [ChessSet] -// allDescend board -// = map (moveKnight board) (possibleMoves board) - -// {-# INLINABLE possibleMoves #-} -// possibleMoves :: ChessSet -> [Direction] -// possibleMoves board -// =[x | x <- [UL,UR,DL,DR,LU,LD,RU,RD], (canMove board x)] - -// {-# INLINABLE deadEnd #-} -// deadEnd :: ChessSet -> Bool -// deadEnd board = (Tx.length (possibleMoves board)) == 0 - -// {-# INLINABLE canJumpFirst #-} -// canJumpFirst :: ChessSet -> Bool -// canJumpFirst board -// = canMoveTo (firstPiece board) (deleteFirst board) - -// {-# INLINABLE tourFinished #-} -// tourFinished :: ChessSet -> Bool -// tourFinished board -// = (noPieces board == (sze*sze)) && (canJumpFirst board) -// where -// sze = sizeBoard board From f2ff9a2ee356959f889897ed272cccba6b3a11b9 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 16 Jul 2024 14:46:36 +0200 Subject: [PATCH 04/13] Review knights benchmark and update dependencies. --- examples/benchmarks/aiken.lock | 4 +- examples/benchmarks/aiken.toml | 2 +- examples/benchmarks/lib/benchmarks/knights.ak | 554 +++++++++++++++++- .../lib/benchmarks/knights/chess_set.ak | 62 +- .../lib/benchmarks/knights/heuristic.ak | 22 +- .../lib/benchmarks/knights/types.ak | 2 +- examples/benchmarks/lib/benchmarks/queue.ak | 29 +- examples/hello_world/aiken.toml | 2 +- examples/hello_world/plutus.json | 2 +- 9 files changed, 555 insertions(+), 124 deletions(-) diff --git a/examples/benchmarks/aiken.lock b/examples/benchmarks/aiken.lock index 0a72eb3c..62f2265a 100644 --- a/examples/benchmarks/aiken.lock +++ b/examples/benchmarks/aiken.lock @@ -3,12 +3,12 @@ [[requirements]] name = "aiken-lang/stdlib" -version = "1.7.0" +version = "1.9.0" source = "github" [[packages]] name = "aiken-lang/stdlib" -version = "1.7.0" +version = "1.9.0" requirements = [] source = "github" diff --git a/examples/benchmarks/aiken.toml b/examples/benchmarks/aiken.toml index 7223d60f..b246c981 100644 --- a/examples/benchmarks/aiken.toml +++ b/examples/benchmarks/aiken.toml @@ -10,5 +10,5 @@ platform = "github" [[dependencies]] name = "aiken-lang/stdlib" -version = "1.7.0" +version = "1.9.0" source = "github" diff --git a/examples/benchmarks/lib/benchmarks/knights.ak b/examples/benchmarks/lib/benchmarks/knights.ak index f6d01146..23926aa5 100644 --- a/examples/benchmarks/lib/benchmarks/knights.ak +++ b/examples/benchmarks/lib/benchmarks/knights.ak @@ -1,42 +1,47 @@ use aiken/list -use benchmarks/knights/heuristic.{descendants, finished_tour, start_tour} +use benchmarks/knights/heuristic.{descendants, start_tour, tour_finished} use benchmarks/knights/types.{ChessSet, Solution} -use benchmarks/queue.{ - Queue, append_all_front, append_front, create_queue, head, is_empty, - remove_front, to_list, +use benchmarks/queue.{Queue} + +// ------------------------------------------------------------------ Benchmarks + +test bench_knights_100_4x4() { + run_knights(100, 4) == [] } -test run_knights0() { - run_knights(100, 0) == [] +test bench_knights_100_6x6() { + run_knights(100, 6) == solution_100_6x6() } -test run_knights1() { - run_knights(100, 2) == [] +test bench_knights_100_8x8() { + run_knights(100, 8) == solution_100_8x8() } +// ----------------------------------------------------------------------- Setup + fn run_knights(depth: Int, board_size: Int) -> Solution { - depth_search(depth, root(board_size), grow, is_fin) |> to_list + depth_search(depth, root(board_size), grow, done) |> queue.to_list } fn depth_search( depth: Int, - queue: Queue, - grow_fn: fn(a) -> List, - fin_fn: fn(a) -> Bool, + xs: Queue, + grow: fn(a) -> List, + done: fn(a) -> Bool, ) -> Queue { - if depth == 0 || is_empty(queue) { - create_queue() - } else if fin_fn(head(queue)) { - depth_search(depth - 1, remove_front(queue), grow_fn, fin_fn) - |> append_front(head(queue)) + if depth == 0 || queue.is_empty(xs) { + queue.new() + } else if done(queue.head(xs)) { + depth_search(depth - 1, queue.remove_front(xs), grow, done) + |> queue.append_front(queue.head(xs)) } else { - append_all_front(remove_front(queue), grow_fn(head(queue))) - |> depth_search(depth - 1, _, grow_fn, fin_fn) + queue.append_all_front(queue.remove_front(xs), grow(queue.head(xs))) + |> depth_search(depth - 1, _, grow, done) } } -fn root(sze: Int) -> Queue<(Int, ChessSet)> { - append_all_front(create_queue(), mk_starts(sze)) +fn root(size: Int) -> Queue<(Int, ChessSet)> { + queue.append_all_front(queue.new(), mk_starts(size)) } fn mk_starts(sze: Int) -> List<(Int, ChessSet)> { @@ -59,13 +64,506 @@ fn interval(a: Int, b: Int) -> List { fn grow(item: (Int, ChessSet)) -> List<(Int, ChessSet)> { let (x, y) = item - - let const_item = x + 1 - descendants(y) |> list.map(fn(list_item) { (const_item, list_item) }) + descendants(y) |> list.map(fn(list_item) { (x + 1, list_item) }) } -fn is_fin(item: (Int, ChessSet)) -> Bool { - let (_, y) = item - - finished_tour(y) +fn done(item: (Int, ChessSet)) -> Bool { + tour_finished(item.2nd) +} + +// ------------------------------------------------------------------ Fixtures + +fn solution_100_6x6() { + [ + ( + 0, + ChessSet { + size: 6, + move_number: 36, + start: Some((1, 1)), + visited: [ + (3, 2), + (5, 3), + (6, 1), + (4, 2), + (3, 4), + (2, 6), + (4, 5), + (6, 6), + (5, 4), + (6, 2), + (4, 1), + (2, 2), + (1, 4), + (3, 3), + (2, 1), + (1, 3), + (2, 5), + (4, 6), + (6, 5), + (4, 4), + (5, 2), + (6, 4), + (5, 6), + (3, 5), + (1, 6), + (2, 4), + (1, 2), + (3, 1), + (4, 3), + (5, 1), + (6, 3), + (5, 5), + (3, 6), + (1, 5), + (2, 3), + (1, 1), + ], + }, + ), + ( + 0, + ChessSet { + size: 6, + move_number: 36, + start: Some((1, 1)), + visited: [ + (3, 2), + (5, 3), + (6, 1), + (4, 2), + (3, 4), + (2, 2), + (4, 1), + (6, 2), + (5, 4), + (6, 6), + (4, 5), + (2, 6), + (1, 4), + (3, 3), + (2, 1), + (1, 3), + (2, 5), + (4, 6), + (6, 5), + (4, 4), + (5, 2), + (6, 4), + (5, 6), + (3, 5), + (1, 6), + (2, 4), + (1, 2), + (3, 1), + (4, 3), + (5, 1), + (6, 3), + (5, 5), + (3, 6), + (1, 5), + (2, 3), + (1, 1), + ], + }, + ), + ( + 0, + ChessSet { + size: 6, + move_number: 36, + start: Some((1, 1)), + visited: [ + (3, 2), + (5, 3), + (6, 1), + (4, 2), + (3, 4), + (2, 2), + (1, 4), + (2, 6), + (4, 5), + (6, 6), + (5, 4), + (6, 2), + (4, 1), + (3, 3), + (2, 1), + (1, 3), + (2, 5), + (4, 6), + (6, 5), + (4, 4), + (5, 2), + (6, 4), + (5, 6), + (3, 5), + (1, 6), + (2, 4), + (1, 2), + (3, 1), + (4, 3), + (5, 1), + (6, 3), + (5, 5), + (3, 6), + (1, 5), + (2, 3), + (1, 1), + ], + }, + ), + ( + 0, + ChessSet { + size: 6, + move_number: 36, + start: Some((1, 1)), + visited: [ + (3, 2), + (5, 3), + (6, 1), + (4, 2), + (3, 4), + (2, 6), + (1, 4), + (2, 2), + (4, 1), + (6, 2), + (5, 4), + (6, 6), + (4, 5), + (3, 3), + (2, 1), + (1, 3), + (2, 5), + (4, 6), + (6, 5), + (4, 4), + (5, 2), + (6, 4), + (5, 6), + (3, 5), + (1, 6), + (2, 4), + (1, 2), + (3, 1), + (4, 3), + (5, 1), + (6, 3), + (5, 5), + (3, 6), + (1, 5), + (2, 3), + (1, 1), + ], + }, + ), + ] +} + +fn solution_100_8x8() { + [ + ( + 0, + ChessSet { + size: 8, + move_number: 64, + start: Some((1, 1)), + visited: [ + (3, 2), + (5, 3), + (4, 5), + (5, 7), + (7, 6), + (8, 8), + (6, 7), + (5, 5), + (3, 4), + (1, 5), + (2, 7), + (4, 8), + (3, 6), + (1, 7), + (3, 8), + (4, 6), + (6, 5), + (4, 4), + (5, 6), + (6, 4), + (7, 2), + (8, 4), + (6, 3), + (5, 1), + (4, 3), + (3, 5), + (5, 4), + (3, 3), + (2, 5), + (1, 3), + (2, 1), + (4, 2), + (6, 1), + (8, 2), + (7, 4), + (8, 6), + (7, 8), + (6, 6), + (8, 5), + (7, 7), + (5, 8), + (3, 7), + (1, 8), + (2, 6), + (1, 4), + (2, 2), + (4, 1), + (6, 2), + (8, 1), + (7, 3), + (5, 2), + (7, 1), + (8, 3), + (7, 5), + (8, 7), + (6, 8), + (4, 7), + (2, 8), + (1, 6), + (2, 4), + (1, 2), + (3, 1), + (2, 3), + (1, 1), + ], + }, + ), + ( + 0, + ChessSet { + size: 8, + move_number: 64, + start: Some((1, 1)), + visited: [ + (3, 2), + (5, 3), + (4, 5), + (5, 7), + (7, 6), + (8, 8), + (6, 7), + (4, 8), + (2, 7), + (1, 5), + (3, 4), + (5, 5), + (3, 6), + (1, 7), + (3, 8), + (4, 6), + (6, 5), + (4, 4), + (5, 6), + (6, 4), + (7, 2), + (8, 4), + (6, 3), + (5, 1), + (4, 3), + (3, 5), + (5, 4), + (3, 3), + (2, 5), + (1, 3), + (2, 1), + (4, 2), + (6, 1), + (8, 2), + (7, 4), + (8, 6), + (7, 8), + (6, 6), + (8, 5), + (7, 7), + (5, 8), + (3, 7), + (1, 8), + (2, 6), + (1, 4), + (2, 2), + (4, 1), + (6, 2), + (8, 1), + (7, 3), + (5, 2), + (7, 1), + (8, 3), + (7, 5), + (8, 7), + (6, 8), + (4, 7), + (2, 8), + (1, 6), + (2, 4), + (1, 2), + (3, 1), + (2, 3), + (1, 1), + ], + }, + ), + ] +} + +fn solution_80_8x8() { + [ + ( + 0, + ChessSet { + size: 8, + move_number: 64, + start: Some((1, 1)), + visited: [ + (3, 2), + (5, 3), + (4, 5), + (5, 7), + (7, 6), + (8, 8), + (6, 7), + (5, 5), + (3, 4), + (1, 5), + (2, 7), + (4, 8), + (3, 6), + (1, 7), + (3, 8), + (4, 6), + (6, 5), + (4, 4), + (5, 6), + (6, 4), + (7, 2), + (8, 4), + (6, 3), + (5, 1), + (4, 3), + (3, 5), + (5, 4), + (3, 3), + (2, 5), + (1, 3), + (2, 1), + (4, 2), + (6, 1), + (8, 2), + (7, 4), + (8, 6), + (7, 8), + (6, 6), + (8, 5), + (7, 7), + (5, 8), + (3, 7), + (1, 8), + (2, 6), + (1, 4), + (2, 2), + (4, 1), + (6, 2), + (8, 1), + (7, 3), + (5, 2), + (7, 1), + (8, 3), + (7, 5), + (8, 7), + (6, 8), + (4, 7), + (2, 8), + (1, 6), + (2, 4), + (1, 2), + (3, 1), + (2, 3), + (1, 1), + ], + }, + ), + ( + 0, + ChessSet { + size: 8, + move_number: 64, + start: Some((1, 1)), + visited: [ + (3, 2), + (5, 3), + (4, 5), + (5, 7), + (7, 6), + (8, 8), + (6, 7), + (4, 8), + (2, 7), + (1, 5), + (3, 4), + (5, 5), + (3, 6), + (1, 7), + (3, 8), + (4, 6), + (6, 5), + (4, 4), + (5, 6), + (6, 4), + (7, 2), + (8, 4), + (6, 3), + (5, 1), + (4, 3), + (3, 5), + (5, 4), + (3, 3), + (2, 5), + (1, 3), + (2, 1), + (4, 2), + (6, 1), + (8, 2), + (7, 4), + (8, 6), + (7, 8), + (6, 6), + (8, 5), + (7, 7), + (5, 8), + (3, 7), + (1, 8), + (2, 6), + (1, 4), + (2, 2), + (4, 1), + (6, 2), + (8, 1), + (7, 3), + (5, 2), + (7, 1), + (8, 3), + (7, 5), + (8, 7), + (6, 8), + (4, 7), + (2, 8), + (1, 6), + (2, 4), + (1, 2), + (3, 1), + (2, 3), + (1, 1), + ], + }, + ), + ] } diff --git a/examples/benchmarks/lib/benchmarks/knights/chess_set.ak b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak index 8d9454b1..30f78440 100644 --- a/examples/benchmarks/lib/benchmarks/knights/chess_set.ak +++ b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak @@ -1,4 +1,3 @@ -use aiken/builtin use aiken/list use benchmarks/knights/types.{ChessSet, Tile} @@ -12,7 +11,6 @@ pub fn create_board(size: Int, init_square: Tile) -> ChessSet { } pub fn add_piece(board: ChessSet, tile: Tile) -> ChessSet { - // record update ChessSet { ..board, move_number: board.move_number + 1, @@ -25,11 +23,6 @@ pub fn first_piece(board: ChessSet) -> Tile { tile } -// {-# INLINABLE lastPiece #-} -// lastPiece :: ChessSet -> Tile -// lastPiece (Board _ _ _ (t:_)) = t -// lastPiece _ = Tx.error () - pub fn last_piece(board: ChessSet) -> Tile { when board.visited is { [] -> fail @@ -70,58 +63,5 @@ fn second_last(visited: List) -> Option { } pub fn is_square_free(board: ChessSet, tile: Tile) -> Bool { - not_in(board.visited, tile) + !list.has(board.visited, tile) } - -pub fn not_in(tiles: List, tile: a) -> Bool { - !list.has(tiles, tile) -} -// {-# INLINABLE positionPiece #-} -// positionPiece :: Integer -> ChessSet -> Tile -// positionPiece x (Board _ n _ ts) = ts Tx.!! (n - x) - -// {-# INLINABLE pieceAtTile #-} -// pieceAtTile :: Tile -> ChessSet -> Integer -// pieceAtTile x0 (Board _ _ _ ts) -// = findPiece x0 ts -// where -// findPiece _ [] = Tx.error () -// findPiece x (y:xs) -// | x == y = 1 + Tx.length xs -// | otherwise = findPiece x xs - -// -- % Everything below here is only needed for printing boards. -// -- % This is useful for debugging. - -// instance Haskell.Show ChessSet where -// showsPrec _ (Board sze n _ ts) -// = Haskell.showString (printBoard sze sortedTrail 1) -// where sortedTrail = quickSort (assignMoveNo ts sze n) - -// assignMoveNo :: [Tile] -> Integer -> Integer -> [Tile] -// assignMoveNo [] _ _ -// = [] -// assignMoveNo ((x,y):t) size z -// = (((y-1)*size)+x,z):assignMoveNo t size (z-1) - -// printBoard :: Integer -> [Tile] -> Integer -> Haskell.String -// printBoard s [] n -// | (n > (s*s)) = "" -// | ((n `Haskell.mod` s) /=0)= "*"++(spaces (s*s) 1) ++(printBoard s [] (n+1)) -// | ((n `Haskell.mod` s) ==0)= "*\n" ++(printBoard s [] (n+1)) -// printBoard s trail@((i,j):xs) n -// | (i==n) && -// ((n `Haskell.mod` s) ==0) = (Haskell.show j)++"\n"++(printBoard s xs (n+1)) -// | (i==n) && -// ((n `Haskell.mod` s) /=0)= (Haskell.show j)++(spaces (s*s) j)++(printBoard s xs (n+1)) -// | ((n `Haskell.mod` s) /=0)= "*" ++(spaces (s*s) 1)++(printBoard s trail (n+1)) -// | ((n `Haskell.mod` s) ==0)= "*\n" ++(printBoard s trail (n+1)) -// printBoard _ _ _ = "?" - -// spaces :: Integer -> Integer -> Haskell.String -// spaces s y = -// take' ((logTen s) - (logTen y) + 1) [' ',' '..] -// where -// logTen :: Integer -> Integer -// logTen 0 = 0 -// logTen x = 1 + logTen (x `Haskell.div` 10) diff --git a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak index 9099b24a..e89b5968 100644 --- a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak +++ b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak @@ -1,9 +1,10 @@ use aiken/builtin +use aiken/int use aiken/list use benchmarks/knights/chess_set.{ add_piece, create_board, delete_first, first_piece, is_square_free, last_piece, } -use benchmarks/knights/sort.{quick_sort} +use benchmarks/knights/sort.{quicksort} use benchmarks/knights/types.{ChessSet, Tile} type Direction { @@ -23,7 +24,6 @@ fn direction_list() { fn move(direction: Direction, tile: Tile) -> Tile { let (x, y) = tile - when direction is { UL -> (x - 1, y - 2) UR -> (x + 1, y - 2) @@ -38,13 +38,11 @@ fn move(direction: Direction, tile: Tile) -> Tile { pub fn start_tour(st: Tile, size: Int) -> ChessSet { expect 0 = builtin.remainder_integer(size, 2) - create_board(size, st) } -pub fn finished_tour(board: ChessSet) -> Bool { +pub fn tour_finished(board: ChessSet) -> Bool { let ChessSet { move_number, size, .. } = board - move_number == size * size && can_jump_first(board) } @@ -78,9 +76,8 @@ pub fn dead_end(board: ChessSet) -> Bool { } pub fn single_descend(board: ChessSet) -> List { - let descendants_and_moves = desc_and_no(board) list.filter_map( - descendants_and_moves, + desc_and_no(board), fn(item) { let (moves, board) = item if moves == 1 { @@ -96,16 +93,15 @@ pub fn desc_and_no(board: ChessSet) -> List<(Int, ChessSet)> { board |> all_descend |> list.map( - fn(item) { - (item |> delete_first |> possible_moves |> list.length, item) - }, - ) + fn(item) { + (item |> delete_first |> possible_moves |> list.length, item) + }, + ) } pub fn can_move_to(board: ChessSet, tile: Tile) -> Bool { let (x, y) = tile let size = board.size - and { x >= 1, x <= size, @@ -122,7 +118,7 @@ fn can_move(board: ChessSet, direction: Direction) -> Bool { pub fn all_descend(board: ChessSet) -> List { board |> possible_moves - |> list.map(fn(direction) { move_knight(board, direction) }) + |> list.map(move_knight(board, _)) } fn move_knight(board: ChessSet, direction: Direction) -> ChessSet { diff --git a/examples/benchmarks/lib/benchmarks/knights/types.ak b/examples/benchmarks/lib/benchmarks/knights/types.ak index fc293456..16b50c6d 100644 --- a/examples/benchmarks/lib/benchmarks/knights/types.ak +++ b/examples/benchmarks/lib/benchmarks/knights/types.ak @@ -4,8 +4,8 @@ pub type Tile = pub type ChessSet { size: Int, move_number: Int, - visited: List, start: Option, + visited: List, } pub type Solution = diff --git a/examples/benchmarks/lib/benchmarks/queue.ak b/examples/benchmarks/lib/benchmarks/queue.ak index 9a5fbb06..0abd9c76 100644 --- a/examples/benchmarks/lib/benchmarks/queue.ak +++ b/examples/benchmarks/lib/benchmarks/queue.ak @@ -4,38 +4,35 @@ pub opaque type Queue { inner: List, } -pub fn create_queue() -> Queue { +pub fn new() -> Queue { [] |> Queue } -pub fn to_list(queue: Queue) -> List { - queue.inner +pub fn to_list(self: Queue) -> List { + self.inner } -pub fn is_empty(queue: Queue) -> Bool { - when queue.inner is { +pub fn is_empty(self: Queue) -> Bool { + when self.inner is { [] -> True _ -> False } } -pub fn append_front(queue: Queue, item: a) -> Queue { - list.push(queue.inner, item) |> Queue +pub fn append_front(self: Queue, item: a) -> Queue { + list.push(self.inner, item) |> Queue } -/// Add all items from the list to the front of the queue -pub fn append_all_front(queue: Queue, items: List) -> Queue { - list.concat(items, queue.inner) |> Queue +pub fn append_all_front(self: Queue, items: List) -> Queue { + list.concat(items, self.inner) |> Queue } -pub fn remove_front(queue: Queue) -> Queue { - expect [_, ..rest] = queue.inner - +pub fn remove_front(self: Queue) -> Queue { + expect [_, ..rest] = self.inner rest |> Queue } -pub fn head(queue: Queue) -> a { - expect [q, ..] = queue.inner - +pub fn head(self: Queue) -> a { + expect [q, ..] = self.inner q } diff --git a/examples/hello_world/aiken.toml b/examples/hello_world/aiken.toml index 1e530f11..3b523f22 100644 --- a/examples/hello_world/aiken.toml +++ b/examples/hello_world/aiken.toml @@ -5,5 +5,5 @@ description = "Aiken contracts for project 'aiken-lang/hello_world'" [[dependencies]] name = "aiken-lang/stdlib" -version = "1.7.0" +version = "main" source = "github" diff --git a/examples/hello_world/plutus.json b/examples/hello_world/plutus.json index 5b6e3cc0..a0fab843 100644 --- a/examples/hello_world/plutus.json +++ b/examples/hello_world/plutus.json @@ -6,7 +6,7 @@ "plutusVersion": "v2", "compiler": { "name": "Aiken", - "version": "v1.0.21-alpha+4b04517" + "version": "v1.0.19-alpha+d56d518" } }, "validators": [ From 65afb1154630c31fd642e643dde86de20db0c8ba Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 16 Jul 2024 14:47:17 +0200 Subject: [PATCH 05/13] Implement quicksort on chess board. Note that it might be easier / cheaper to serialize and compare bytes? --- .../lib/benchmarks/knights/heuristic.ak | 73 ++++++++++++++++++- .../benchmarks/lib/benchmarks/knights/sort.ak | 20 ++++- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak index e89b5968..2c36662d 100644 --- a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak +++ b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak @@ -59,7 +59,11 @@ pub fn descendants(board: ChessSet) -> List { let singles = single_descend(board) when singles is { - [] -> board |> desc_and_no |> quick_sort |> list.map(builtin.snd_pair) + [] -> + board + |> desc_and_no + |> quicksort(compare_chess_set) + |> list.map(fn(t) { t.2nd }) [_] -> singles _ -> [] @@ -126,5 +130,70 @@ fn move_knight(board: ChessSet, direction: Direction) -> ChessSet { } fn possible_moves(board: ChessSet) -> List { - direction_list() |> list.filter(fn(direction) { can_move(board, direction) }) + direction_list() |> list.filter(can_move(board, _)) +} + +fn compare_tile(a: Tile, b: Tile) -> Ordering { + if a.1st == b.1st { + int.compare(a.2nd, b.2nd) + } else { + int.compare(a.1st, b.1st) + } +} + +fn compare_list(xs: List, ys: List) -> Ordering { + when xs is { + [] -> + when ys is { + [] -> Equal + _ -> Less + } + [x, ..xs] -> + when ys is { + [] -> Greater + [y, ..ys] -> { + let ord = compare_tile(x, y) + if ord == Equal { + compare_list(xs, ys) + } else { + ord + } + } + } + } +} + +fn compare_chess_set(a: (Int, ChessSet), b: (Int, ChessSet)) -> Ordering { + if a.1st != b.1st { + int.compare(a.1st, b.1st) + } else { + let ChessSet { + size: size_a, + move_number: move_a, + start: start_a, + visited: visited_a, + } = a.2nd + let ChessSet { + size: size_b, + move_number: move_b, + start: start_b, + visited: visited_b, + } = b.2nd + if size_a != size_b { + int.compare(size_a, size_b) + } else if move_a != move_b { + int.compare(move_a, move_b) + } else if start_a != start_b { + when start_a is { + Some(a) -> + when start_b is { + Some(b) -> compare_tile(a, b) + None -> Greater + } + None -> Less + } + } else { + compare_list(visited_a, visited_b) + } + } } diff --git a/examples/benchmarks/lib/benchmarks/knights/sort.ak b/examples/benchmarks/lib/benchmarks/knights/sort.ak index 6e429da9..40a59c8f 100644 --- a/examples/benchmarks/lib/benchmarks/knights/sort.ak +++ b/examples/benchmarks/lib/benchmarks/knights/sort.ak @@ -1,3 +1,19 @@ -pub fn quick_sort(l: List) -> List { - todo +use aiken/list + +pub fn quicksort(xs: List, compare: fn(a, a) -> Ordering) -> List { + when xs is { + [] -> + [] + [head, ..tail] -> { + let before = + tail + |> list.filter(fn(x) { compare(x, head) == Less }) + |> quicksort(compare) + let after = + tail + |> list.filter(fn(x) { compare(x, head) != Less }) + |> quicksort(compare) + list.concat(before, [head, ..after]) + } + } } From 5b0a1716b574f1bb4bf974ca47bacd349efb8745 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 16 Jul 2024 14:48:51 +0200 Subject: [PATCH 06/13] Fix mk_starts and second_last in knights benchmarks. mk_starts was not yielding enough values. It's originally a translation of a double list comprehension in Haskell, which cannot simply be translated to a map2. The latter combine elements two by two, but the former works through all possible permutations. --- examples/benchmarks/lib/benchmarks/knights.ak | 16 +++++++++---- .../lib/benchmarks/knights/chess_set.ak | 23 +++++-------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/examples/benchmarks/lib/benchmarks/knights.ak b/examples/benchmarks/lib/benchmarks/knights.ak index 23926aa5..6aa16529 100644 --- a/examples/benchmarks/lib/benchmarks/knights.ak +++ b/examples/benchmarks/lib/benchmarks/knights.ak @@ -44,14 +44,20 @@ fn root(size: Int) -> Queue<(Int, ChessSet)> { queue.append_all_front(queue.new(), mk_starts(size)) } -fn mk_starts(sze: Int) -> List<(Int, ChessSet)> { - let x_list = interval(1, sze) - let y_list = interval(1, sze) +fn mk_starts(size: Int) -> List<(Int, ChessSet)> { + let it = interval(1, size) + + let l = + list.flat_map( + it, + fn(x) { list.map(it, fn(y) { start_tour((x, y), size) }) }, + ) - let l = x_list |> list.map2(y_list, fn(a, b) { start_tour((a, b), sze) }) let length = list.length(l) - list.repeat(1 - length, length) |> list.zip(l) + expect length == size * size + + list.zip(list.repeat(1 - length, length), l) } fn interval(a: Int, b: Int) -> List { diff --git a/examples/benchmarks/lib/benchmarks/knights/chess_set.ak b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak index 30f78440..5639dacc 100644 --- a/examples/benchmarks/lib/benchmarks/knights/chess_set.ak +++ b/examples/benchmarks/lib/benchmarks/knights/chess_set.ak @@ -33,32 +33,21 @@ pub fn last_piece(board: ChessSet) -> Tile { pub fn delete_first(board: ChessSet) -> ChessSet { let ChessSet { move_number, visited, .. } = board - expect Some(deleted_first) = list.init(visited) + expect Some(new_visited) = list.init(visited) ChessSet { ..board, move_number: move_number - 1, start: second_last(visited), - visited: deleted_first, + visited: new_visited, } } fn second_last(visited: List) -> Option { - when visited is { - [] -> None - [_, ..rest] -> { - let value = second_last(rest) - - if value == None { - if builtin.null_list(rest) { - None - } else { - Some(builtin.head_list(visited)) - } - } else { - value - } - } + when list.reverse(visited) is { + [] -> fail + [_] -> None + [_, a, ..] -> Some(a) } } From f14dab9547a63fcf664244e3305acdff7f7e0fc7 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 16 Jul 2024 17:13:03 +0200 Subject: [PATCH 07/13] Collect results from previous versions in a README. --- examples/benchmarks/README.md | 91 +++-- examples/benchmarks/lib/benchmarks/knights.ak | 361 +++++++----------- .../lib/benchmarks/knights/heuristic.ak | 43 +-- 3 files changed, 200 insertions(+), 295 deletions(-) diff --git a/examples/benchmarks/README.md b/examples/benchmarks/README.md index 3564414e..54644eea 100644 --- a/examples/benchmarks/README.md +++ b/examples/benchmarks/README.md @@ -1,55 +1,66 @@ -# benchmarks +# (No Fib) Benchmarks -Write validators in the `validators` folder, and supporting functions in the `lib` folder using `.ak` as a file extension. +This folder contains a set of benchmarks inspired and ported from the [plutus-benchmarks](https://github.com/IntersectMBO/plutus/tree/master/plutus-benchmark/nofib#plutus-nofib-benchmarks), written in Haskell. The idea is to provide benchmarks which can capture more realistic patterns and behaviours than usual "Fibonacci" benchmarks often used for benchmarking applications but falling short in capturing real-world scenarios. -For example, as `validators/always_true.ak` +Note that the primary use-case of those benchmarks is to compare Aiken with itself across compiler versions. As optimizations get implemented, it comes as a supplimentary means to assess their impact. -```gleam -validator { - fn spend(_datum: Data, _redeemer: Data, _context: Data) -> Bool { - True - } -} -``` +## Summary -## Building +Results are summarized below, relatively to the previous version. For brevity, we only report versions for which there's a deviation from a previous version. -```sh -aiken build -``` + -```sh -aiken check -m foo -``` +### CPU -## Documentation +| Benchmark | `v1.0.29` | vs `v1.0.25` | vs `v1.0.23` | vs `v1.0.21` | vs `v1.0.18` | +| --- | ---: | ---: | ---: | ---: | ---: | +| `knights_100_4x4` | 71851995726 | +1.40% | +4.45% | +4.30% | +4.80% | +| `knights_100_6x6` | 159767368294 | +8.93% | +11.92% | +11.85% | +12.70% | +| `knights_100_8x8` | 319834775948 | +11.40% | +14.35% | +14.32% | +15.13% | -If you're writing a library, you might want to generate an HTML documentation for it. +### Mem -Use: - -```sh -aiken docs -``` - -## Resources - -Find more on the [Aiken's user manual](https://aiken-lang.org). +| Benchmark | `v1.0.29` | vs `v1.0.25` | vs `v1.0.23` | vs `v1.0.21` | vs `v1.0.18` | +| --- | ---: | ---: | ---: | ---: | ---: | +| `knights_100_4x4` | 172256715 | -0.48% | +5.04% | +4.90% | +5.80% | +| `knights_100_6x6` | 321712271 | +10.08% | +16.54% | +16.46% | +18.29% | +| `knights_100_8x8` | 601065675 | +14.48% | +21.30% | +21.26% | +23.15% | diff --git a/examples/benchmarks/lib/benchmarks/knights.ak b/examples/benchmarks/lib/benchmarks/knights.ak index 6aa16529..7e9374b6 100644 --- a/examples/benchmarks/lib/benchmarks/knights.ak +++ b/examples/benchmarks/lib/benchmarks/knights.ak @@ -79,7 +79,7 @@ fn done(item: (Int, ChessSet)) -> Bool { // ------------------------------------------------------------------ Fixtures -fn solution_100_6x6() { +fn solution_100_6x6() -> Solution { [ ( 0, @@ -268,7 +268,7 @@ fn solution_100_6x6() { ] } -fn solution_100_8x8() { +fn solution_100_8x8() -> Solution { [ ( 0, @@ -278,32 +278,36 @@ fn solution_100_8x8() { start: Some((1, 1)), visited: [ (3, 2), - (5, 3), - (4, 5), - (5, 7), - (7, 6), - (8, 8), - (6, 7), - (5, 5), - (3, 4), - (1, 5), - (2, 7), - (4, 8), - (3, 6), - (1, 7), - (3, 8), - (4, 6), - (6, 5), (4, 4), (5, 6), (6, 4), - (7, 2), + (8, 5), + (7, 7), + (6, 5), (8, 4), + (7, 2), + (5, 3), + (3, 4), + (4, 6), + (5, 8), + (6, 6), + (4, 5), + (3, 7), + (1, 8), + (2, 6), + (4, 7), + (5, 5), (6, 3), (5, 1), (4, 3), (3, 5), (5, 4), + (7, 3), + (8, 1), + (6, 2), + (4, 1), + (2, 2), + (1, 4), (3, 3), (2, 5), (1, 3), @@ -314,31 +318,27 @@ fn solution_100_8x8() { (7, 4), (8, 6), (7, 8), - (6, 6), - (8, 5), - (7, 7), - (5, 8), - (3, 7), - (1, 8), - (2, 6), - (1, 4), - (2, 2), - (4, 1), - (6, 2), - (8, 1), - (7, 3), + (5, 7), + (3, 8), + (1, 7), + (3, 6), + (2, 8), + (1, 6), + (2, 4), + (1, 2), + (3, 1), (5, 2), (7, 1), (8, 3), (7, 5), (8, 7), (6, 8), - (4, 7), - (2, 8), - (1, 6), - (2, 4), - (1, 2), - (3, 1), + (7, 6), + (8, 8), + (6, 7), + (4, 8), + (2, 7), + (1, 5), (2, 3), (1, 1), ], @@ -352,32 +352,36 @@ fn solution_100_8x8() { start: Some((1, 1)), visited: [ (3, 2), - (5, 3), - (4, 5), - (5, 7), - (7, 6), - (8, 8), - (6, 7), - (4, 8), - (2, 7), - (1, 5), - (3, 4), - (5, 5), - (3, 6), - (1, 7), - (3, 8), - (4, 6), - (6, 5), (4, 4), (5, 6), + (7, 7), + (8, 5), (6, 4), (7, 2), (8, 4), + (6, 5), + (5, 3), + (3, 4), + (4, 6), + (5, 8), + (6, 6), + (4, 5), + (3, 7), + (1, 8), + (2, 6), + (4, 7), + (5, 5), (6, 3), (5, 1), (4, 3), (3, 5), (5, 4), + (7, 3), + (8, 1), + (6, 2), + (4, 1), + (2, 2), + (1, 4), (3, 3), (2, 5), (1, 3), @@ -388,184 +392,101 @@ fn solution_100_8x8() { (7, 4), (8, 6), (7, 8), - (6, 6), - (8, 5), - (7, 7), - (5, 8), - (3, 7), - (1, 8), - (2, 6), - (1, 4), - (2, 2), - (4, 1), - (6, 2), - (8, 1), - (7, 3), - (5, 2), - (7, 1), - (8, 3), - (7, 5), - (8, 7), - (6, 8), - (4, 7), - (2, 8), - (1, 6), - (2, 4), - (1, 2), - (3, 1), - (2, 3), - (1, 1), - ], - }, - ), - ] -} - -fn solution_80_8x8() { - [ - ( - 0, - ChessSet { - size: 8, - move_number: 64, - start: Some((1, 1)), - visited: [ - (3, 2), - (5, 3), - (4, 5), - (5, 7), - (7, 6), - (8, 8), - (6, 7), - (5, 5), - (3, 4), - (1, 5), - (2, 7), - (4, 8), - (3, 6), - (1, 7), - (3, 8), - (4, 6), - (6, 5), - (4, 4), - (5, 6), - (6, 4), - (7, 2), - (8, 4), - (6, 3), - (5, 1), - (4, 3), - (3, 5), - (5, 4), - (3, 3), - (2, 5), - (1, 3), - (2, 1), - (4, 2), - (6, 1), - (8, 2), - (7, 4), - (8, 6), - (7, 8), - (6, 6), - (8, 5), - (7, 7), - (5, 8), - (3, 7), - (1, 8), - (2, 6), - (1, 4), - (2, 2), - (4, 1), - (6, 2), - (8, 1), - (7, 3), - (5, 2), - (7, 1), - (8, 3), - (7, 5), - (8, 7), - (6, 8), - (4, 7), - (2, 8), - (1, 6), - (2, 4), - (1, 2), - (3, 1), - (2, 3), - (1, 1), - ], - }, - ), - ( - 0, - ChessSet { - size: 8, - move_number: 64, - start: Some((1, 1)), - visited: [ - (3, 2), - (5, 3), - (4, 5), - (5, 7), - (7, 6), - (8, 8), - (6, 7), - (4, 8), - (2, 7), - (1, 5), - (3, 4), - (5, 5), - (3, 6), - (1, 7), - (3, 8), - (4, 6), - (6, 5), - (4, 4), - (5, 6), - (6, 4), - (7, 2), - (8, 4), - (6, 3), - (5, 1), - (4, 3), - (3, 5), - (5, 4), - (3, 3), - (2, 5), - (1, 3), - (2, 1), - (4, 2), - (6, 1), - (8, 2), - (7, 4), - (8, 6), - (7, 8), - (6, 6), - (8, 5), - (7, 7), - (5, 8), - (3, 7), - (1, 8), - (2, 6), - (1, 4), - (2, 2), - (4, 1), - (6, 2), - (8, 1), - (7, 3), + (5, 7), + (3, 8), + (1, 7), + (3, 6), + (2, 8), + (1, 6), + (2, 4), + (1, 2), + (3, 1), (5, 2), (7, 1), (8, 3), (7, 5), (8, 7), (6, 8), + (7, 6), + (8, 8), + (6, 7), + (4, 8), + (2, 7), + (1, 5), + (2, 3), + (1, 1), + ], + }, + ), + ( + 0, + ChessSet { + size: 8, + move_number: 64, + start: Some((1, 1)), + visited: [ + (3, 2), + (4, 4), + (6, 5), + (8, 4), + (7, 2), + (5, 3), + (3, 4), + (4, 6), + (5, 8), + (7, 7), + (5, 6), + (6, 4), + (8, 5), + (6, 6), + (4, 5), + (3, 7), + (1, 8), + (2, 6), (4, 7), + (5, 5), + (6, 3), + (5, 1), + (4, 3), + (3, 5), + (5, 4), + (7, 3), + (8, 1), + (6, 2), + (4, 1), + (2, 2), + (1, 4), + (3, 3), + (2, 5), + (1, 3), + (2, 1), + (4, 2), + (6, 1), + (8, 2), + (7, 4), + (8, 6), + (7, 8), + (5, 7), + (3, 8), + (1, 7), + (3, 6), (2, 8), (1, 6), (2, 4), (1, 2), (3, 1), + (5, 2), + (7, 1), + (8, 3), + (7, 5), + (8, 7), + (6, 8), + (7, 6), + (8, 8), + (6, 7), + (4, 8), + (2, 7), + (1, 5), (2, 3), (1, 1), ], diff --git a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak index 2c36662d..efcfb2b1 100644 --- a/examples/benchmarks/lib/benchmarks/knights/heuristic.ak +++ b/examples/benchmarks/lib/benchmarks/knights/heuristic.ak @@ -133,11 +133,11 @@ fn possible_moves(board: ChessSet) -> List { direction_list() |> list.filter(can_move(board, _)) } -fn compare_tile(a: Tile, b: Tile) -> Ordering { - if a.1st == b.1st { - int.compare(a.2nd, b.2nd) - } else { +fn compare_chess_set(a: (Int, ChessSet), b: (Int, ChessSet)) -> Ordering { + if a.1st != b.1st { int.compare(a.1st, b.1st) + } else { + compare_list(a.2nd.visited, b.2nd.visited) } } @@ -163,37 +163,10 @@ fn compare_list(xs: List, ys: List) -> Ordering { } } -fn compare_chess_set(a: (Int, ChessSet), b: (Int, ChessSet)) -> Ordering { - if a.1st != b.1st { - int.compare(a.1st, b.1st) +fn compare_tile(a: Tile, b: Tile) -> Ordering { + if a.1st == b.1st { + int.compare(a.2nd, b.2nd) } else { - let ChessSet { - size: size_a, - move_number: move_a, - start: start_a, - visited: visited_a, - } = a.2nd - let ChessSet { - size: size_b, - move_number: move_b, - start: start_b, - visited: visited_b, - } = b.2nd - if size_a != size_b { - int.compare(size_a, size_b) - } else if move_a != move_b { - int.compare(move_a, move_b) - } else if start_a != start_b { - when start_a is { - Some(a) -> - when start_b is { - Some(b) -> compare_tile(a, b) - None -> Greater - } - None -> Less - } - } else { - compare_list(visited_a, visited_b) - } + int.compare(a.1st, b.1st) } } From 28d2ee7186ce421e3b5b11f8686900318d6112ed Mon Sep 17 00:00:00 2001 From: Matthias Benkort <5680256+KtorZ@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:13:49 +0200 Subject: [PATCH 08/13] Delete examples/benchmarks/.github/workflows/tests.yml --- .../benchmarks/.github/workflows/tests.yml | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 examples/benchmarks/.github/workflows/tests.yml diff --git a/examples/benchmarks/.github/workflows/tests.yml b/examples/benchmarks/.github/workflows/tests.yml deleted file mode 100644 index 89136858..00000000 --- a/examples/benchmarks/.github/workflows/tests.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Tests - -on: - push: - branches: ["main"] - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: aiken-lang/setup-aiken@v0.1.0 - with: - version: v1 - - - run: aiken fmt --check - - run: aiken check -D - - run: aiken build From 20e606e64527a01eeb304426f75bbf42d7c2aaf8 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Tue, 16 Jul 2024 17:16:11 +0200 Subject: [PATCH 09/13] Add benchmarks to continuous integration workflow. --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f374670d..31db2fb4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,6 +18,7 @@ jobs: run: | cargo run -r -- check examples/hello_world cargo run -r -- check examples/gift_card + cargo run -r -- check examples/benchmarks - name: Run acceptance tests working-directory: examples/acceptance_tests run: | From 52974aed7521cff541c3124cf4a0b7fa0f20993b Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 17 Jul 2024 12:51:58 +0200 Subject: [PATCH 10/13] Add clausify benchmark. --- examples/benchmarks/README.md | 87 +++++ .../lib/benchmarks/clausify/benchmark.ak | 304 ++++++++++++++++++ .../{knights.ak => knights/benchmark.ak} | 2 +- .../lib/benchmarks/{ => knights}/queue.ak | 0 4 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 examples/benchmarks/lib/benchmarks/clausify/benchmark.ak rename examples/benchmarks/lib/benchmarks/{knights.ak => knights/benchmark.ak} (99%) rename examples/benchmarks/lib/benchmarks/{ => knights}/queue.ak (100%) diff --git a/examples/benchmarks/README.md b/examples/benchmarks/README.md index 54644eea..25cbf09a 100644 --- a/examples/benchmarks/README.md +++ b/examples/benchmarks/README.md @@ -8,8 +8,49 @@ Note that the primary use-case of those benchmarks is to compare Aiken with itse Results are summarized below, relatively to the previous version. For brevity, we only report versions for which there's a deviation from a previous version. + +