Giter Site home page Giter Site logo

kmyk-jikka / jikka Goto Github PK

View Code? Open in Web Editor NEW
152.0 5.0 11.0 78.95 MB

an automated solver for problems of competitive programming

Home Page: https://kmyk-jikka.github.io/Jikka/playground

License: Apache License 2.0

Haskell 91.85% C++ 1.85% Python 2.74% Shell 0.39% Yacc 3.17%
competitive-programming programming-contests programming-language transpiler compiler optimization algorithms

jikka's Introduction

Jikka

test

Jikka is an automated solver for problems of competitive programming.

In competitive programming, there are some problems that can be solved just by repeating formula transformations or by pasting snippets of famous data structures. Jikka solves such problems automatically. Jikka takes problems as input in the form of programs of a very restricted subset of Python, optimizes the codes to reduce their computational complexity, and converts them to implementations of C++ for output. / 競技プログラミングにおいて「ただ式変形をするだけで解ける」「ただデータ構造のライブラリを貼るだけで解ける」問題は実は少なくありません。 Jikka はそのような問題を自動で解いてくれます。 Jikka は問題をとても制限された Python のサブセット言語のコードの形で入力として受け取り、計算量を落とすような最適化を行い、C++ の実装に変換して出力します。

Try on Web

Go https://kmyk.github.io/Jikka/playground.

Usage

$ jikka convert PYTHON_FILE

How to Install

Using prebuilt binaries (recommended)

Go Releases page and download jikka-vA.B.C.D-Linux, jikka-vA.B.C.D-maxOS or jikka-vA.B.C.D-Windows.exe.

Using Stack

Stack is required. If you are using Ubuntu, you can install Stack with $ sudo apt install haskell-stack.

$ git clone https://github.com/kmyk/Jikka.git
$ cd Jikka
$ stack install

Using Cabal

Cabal is required. This is bundled Haskell Platform. If you are using Ubuntu, you can install Stack with $ sudo apt install haskell-platform.

$ cabal update
$ cabal install Jikka

Discussions

Please feel free to use our GitHub Discussions. / GitHub Discussions があるので気軽に使ってください。

Documents

For users:

For developpers:

Blog articles:

Examples

examples/fib.py (v5.0.5.0)

Input, O(N):

def f(n: int) -> int:
    a = 0
    b = 1
    for _ in range(n):
        c = a + b
        a = b
        b = c
    return a

def solve(n: int) -> int:
    return f(n) % 1000000007

Output, O(log N):

#include "jikka/all.hpp"
#include <algorithm>
#include <cstdint>
#include <functional>
#include <iostream>
#include <numeric>
#include <string>
#include <tuple>
#include <vector>
int64_t solve(int64_t n_317) {
  return jikka::modmatap<2, 2>(
      jikka::modmatpow<2>(jikka::make_array<std::array<int64_t, 2>>(
                              jikka::make_array<int64_t>(1, 1),
                              jikka::make_array<int64_t>(1, 0)),
                          n_317, 1000000007),
      jikka::make_array<int64_t>(1, 0), 1000000007)[1];
}
int main() {
  int64_t x318;
  std::cin >> x318;
  int64_t x319 = solve(x318);
  std::cout << x319;
  std::cout << '\n';
}

examples/static_range_sum.py (v5.0.10.0)

Input, O(N^2):

# https://judge.yosupo.jp/problem/static_range_sum

from typing import *

def solve(n: int, q: int, a: List[int], l: List[int], r: List[int]) -> List[int]:
    ans = [-1 for _ in range(q)]
    for i in range(q):
        ans[i] = sum(a[l[i]:r[i]])
    return ans

def main() -> None:
    n, q = map(int, input().split())
    a = list(map(int, input().split()))
    assert len(a) == n
    l = list(range(q))
    r = list(range(q))
    for i in range(q):
        l[i], r[i] = map(int, input().split())
    ans = solve(n, q, a, l, r)
    for i in range(q):
        print(ans[i])

if __name__ == '__main__':
    main()

Output, O(N):

#include <algorithm>
#include <cstdint>
#include <functional>
#include <iostream>
#include <numeric>
#include <string>
#include <tuple>
#include <vector>
std::vector<int64_t> solve(int64_t n_0, int64_t q_1, std::vector<int64_t> a_2,
                           std::vector<int64_t> l_3, std::vector<int64_t> r_4) {
  std::vector<int64_t> x6 = std::vector<int64_t>(a_2.size() + 1);
  x6[0] = 0;
  for (int32_t i7 = 0; i7 < int32_t(a_2.size()); ++i7) {
    x6[(i7 + 1)] = x6[i7] + a_2[i7];
  }
  std::vector<int64_t> x5 = x6;
  std::vector<int64_t> x10 = std::vector<int64_t>(q_1);
  for (int32_t i11 = 0; i11 < int32_t(q_1); ++i11) {
    x10[i11] = -x5[l_3[i11]] + x5[r_4[i11]];
  }
  return x10;
}
int main() {
  int64_t n_13 = -1;
  int64_t q_14 = -1;
  std::cin >> n_13;
  std::vector<int64_t> a_15 = std::vector<int64_t>(n_13, -1);
  std::cin >> q_14;
  std::vector<int64_t> l_16 = std::vector<int64_t>(q_14, -1);
  std::vector<int64_t> r_17 = std::vector<int64_t>(q_14, -1);
  for (int32_t i18 = 0; i18 < n_13; ++i18) {
    std::cin >> a_15[i18];
  }
  for (int32_t i_19 = 0; i_19 < q_14; ++i_19) {
    std::cin >> l_16[i_19];
    std::cin >> r_17[i_19];
  }
  for (int32_t i_20 = 0; i_20 < q_14; ++i_20) {
  }
  auto ans_21 = solve(n_13, q_14, a_15, l_16, r_17);
  for (int32_t i_22 = 0; i_22 < q_14; ++i_22) {
  }
  for (int32_t i_23 = 0; i_23 < q_14; ++i_23) {
    std::cout << ans_21[i_23] << ' ';
    std::cout << '\n' << ' ';
  }
}

License

Appache License 2.0

jikka's People

Contributors

hotman78 avatar kmyk avatar koki-yamaguchi avatar riantkb avatar rsk0315 avatar uta8a avatar zer0-star avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

jikka's Issues

assert を core や C++ で利用する

assert n == len(a) を利用して map(lambda i: a[i], range(n))a に変換するなどしたい
いまは restricted Python → core の変換時に無視してしまってる

TODO: 型について

構文解析はだいたいできたので、型が付いていない λ 項が入力されていると見ることができる。したいのは式木を組み換えての最適化処理。なのでとりあえずいい感じに型再構築が必要。

Convert recursions to loops

再帰よりループの方が最適化しやすいのでほしい。

末尾再帰最適化というよりも停止性自動証明をやることになる。
ad-hoc に induction measure (つまり DP table の埋める順番) を総当たりして潰すことになりそう。

たとえば

def fact(n):
    if n == 0:
        return 1
    else:
        return n * fact(n - 1)

def fact(n):
    dp = [-1 for _ in range(n + 1)]
    dp[0] = 1
    for i in range(n):
        dp[n + 1] = (n + 1) * dp[n]
    return dp[n]

になってほしい。

Erase if-exprs in loops

邪魔なので消去したいのだけど、一般的な方法で消去するのが難しすぎて困っています。
ad-hoc に処理することはできるけどしんどいし役立つのか不明だしなのでできれば避けたい。

やること:

  1. 追加の具体例を収集する
  2. いい感じの方法を考える

例 1

# https://atcoder.jp/contests/abc127/tasks/abc127_e

MOD: int = 10 ** 9 + 7

def solve(h: int, w: int, k: int) -> int:
    ans = 0
    for y1 in range(h):
        for x1 in range(w):
            for y2 in range(h):
                for x2 in range(w):
                    if (y1, x1) < (y2, x2):
                        ans += choose(h * w - 2, k - 2) * (abs(y2 - y1) + abs(x2 - x1))
    return ans % MOD

例 2

# https://atcoder.jp/contests/abc134/tasks/abc134_c
from typing import *

def solve(N: int, A: List[int]) -> List[int]:
    assert 2 <= N <= 200000
    assert len(A) == N
    assert all(0 <= A_i <= 200000 for A_i in A)

    ans = [-1 for _ in range(N)]
    for i in range(N):
        ans[i] = max((0 if j == i else A[j]) for j in range(N))
    return ans

入出力部分も生成する

選択肢は以下のふたつ。いまのところは入力コードの self-contained のために (1.) を推したい

  1. a = int(input()) みたいな入力取得コードを解釈して変換する
  2. コメントなどで N A_1 A_2 ... A_N みたいなのを書かせて生成する

同種の let があればまとめる

Description / 説明

もし

let x = e(...)
in let y = e(...)
in f(x, y)

があったら

let x = e(...)
in f(x, x)

にする

Motivation / 動機

定数倍しか変わらないけど、違うループの中で別々に作られた同じ累積和とかはまとめたい

for i in range(n):
   ans += sum(a[:i])
for i in range(n):
   ans += sum(a[:i])

Use affine types internally

C++ には計算量を考えたときに contraction が可能な型と不可能な型とがある。たとえば vector<int> xs; のとき vector<int> ys = xs; ys.push_back(1); return f(ys, ys); なら move を使って O(1) だが、vector<int> ys = xs; ys.push_back(1); return f(xs, ys); ならば copy になって O(n) である。
この区別を中間言語あるいは Python の上でも持ちたい。contraction 不可能な型についての contraction を検出してエラーにし、そのような contraction をする rewrite rules を禁止して防ぎたい。

このために affine 型を利用したい。
依存型などは悪手だと考えているが、affine 型や線形型なら分かりやすいのでありだと思う。Rust での実績もある。

FFT

REP (i, n) REP (j, n) c[i + j] += a[i] * b[j] があったら c = fft(a, b) にするやつ

  • core 言語に新しい組み込み関数 FFT InverseFFT みたいなやつを足す (あるいは Convolution という組み込み関数を足す?) (Jikka.Core.Language.Expr)
    • それらが $ jikka execute ... で実行できるようにする (Jikka.Core.Execute)
    • それらが C++ に変換されるようにする (Jikka.CPlusPlus.Convert.FromCore)
    • 必要なら runtime/include/jikka/convoluion.hpp を足して実装する
    • それらが C++ で使われていたら自動で #include "jikka/convolution.hpp" とか #include <atcoder/convolution> されるようにする (Jikka.CPlusPlus.Format)
    • その他必要なもろもろをやる (コンパイルエラーや警告が出なくなれば OK)
  • core 言語に、foldl (fun c i -> foldl (fun c j -> setAt c (i + j) a[i] b[j]) c (range n)) c (range n) やあるいはこれと等価な部分式があれば、それを FFT を使って書き直すような rewrite rule を足す (Jikka.Core.Convert.FFT みたいなのを足して Jikka.Core.Convert の中から呼び出す)

rewrite rule をもっと簡単に書けるようにする

GHC の RULES pragma みたいに手軽に書きたい

{-# RULES
  "map/map"    forall f g xs.  map f (map g xs) = map (f.g) xs
    #-}

Template Haskell を使って準クオートで書けるようにするのがよい気がする

mapMapReduce :: Monad m => RewriteRule m
mapMapReduce = [rule| "map/map"  forall f g xs.  map f (map g xs) = map (f.g) xs |]

Binary Indexed Tree (Fenwick Tree)

Currently we always use segment trees, but we replace them with binary indexed trees if possible because they have a little bit smaller constant-time factor.

添字の中の演算子に無駄な括弧が付いてる

h_1[(i3 + 1)] でなくて h3[i3 + 1] ってなっててほしい

現状:

          std::min<int64_t>(
              x2[i3][1] + std::max<int64_t>(-h_1[(i3 + 1)] + h_1[(i3 + 2)],
                                            h_1[(i3 + 1)] - h_1[(i3 + 2)]),
              x2[i3][0] + std::max<int64_t>(-h_1[i3] + h_1[(i3 + 2)],
                                            h_1[i3] - h_1[(i3 + 2)]))};

メモ: 哲学について

方向付けのために胡乱なことを書いていたが、人目に付くところに置いておいても混乱を招くだけでよくなさそうなので消した。しかし重要なものでもあると考えているので、 Issue として残しておく。
4d6e5c3

四則演算などの式を整理する

-1 + a + b - a + 2b + 1 になってほしい
Jikka.Core.Language.ArithmeticalExpr をするだけなのだけど、局所的な書き換えでは済まないから rewrite rule には落ちなさそう

segment tree を貼る

累積和を取ってある箇所を検出して、その累積和に対する更新が発生しているなら大域的に segment tree に書き換える、とかがよいかと思われる

Refactoring for v5.0.2.0

とりあえず動かすことを優先で書いたものが v5.0.1.0 ですが、この上にものを積むには汚すぎてしんどいので仕様の再検討と実装の書き直しが必要そう。

  • 本物の Python をそのままパースできるようにする
    • 構文解析だけなら簡単だということが分かってきたので
  • Python のサブセット言語の仕様を修正し簡単化する
    • 「非負である」「要素数は n である」みたいな情報を無理矢理に型システムに押し込む利点はあまりない気がしてきたので
  • できるだけ広い範囲の AST により詳細な位置情報を乗せる
    • エラー報告は実用上かなり重要だし後から付け加えるのはしんどいので早めにやるべき
  • 一貫性のあるエラー処理機構を用意する
    • Either String a で頑張っているが、つらい
  • rewrite rules を簡単に定義できる DSL を用意する
    • 現状では、記述が面倒。しかも AST に対する型検査は走らないのでバグが埋まりやすい
  • modules の構造を転置する
    • Jikka.Converter.CoreJikka.Core.Converter にするなど

機械的に解けそうな問題を収集する

集めています。テストに使います。見つけたら教えてください。可能なら Python の愚直解やテストケースをプルリクしてくれるとよりうれしいです。


  • 自動で解けたらチェックを入れていきたい

やること

言語機能

  • liquid types を実装
    • 添字の境界検査とかを自動でしたいと思うとこの手のものは避けられなさそう
    • #2 を参照
  • 実装を進めて機能不足を埋める
  • 最適化の際に使いやすい構文木を設計する
  • Haskell 系の文法の方が数式ぽくてよさそうなので修正する
    • 現在は型システムの資料の多さのために一度ML に戻してある状態

最適化

  • 最適化のルールを追加しやすいような枠組みを作る
  • 定数畳み込みや式変形などの実装を進める
  • データ構造を貼るやつを実装
  • 再帰的定義を分析してループに直したり依存関係を判断したりするやつを実装
  • 普段の精進の中で「あー こういう考察は機械的にできそうだなあ」みたいなやつを見つけて集める

出力

  • C++ のものの実装を進める
  • 計算量解析をする
    • 補助として使うなら「ここをこう変えると計算量どうなる?」みたいな質問ができるとうれしいはず
    • C++ から ML への変換器と併わせて、エディタのプラグインにして常に現在のコードの計算量が表示されるようになったらうれしい

周辺ツール

  • C++ から ML 形式への変換器を書く
    • エンドユーザは C++ しか書けないだろうのであるとよい
  • 入出力パートの自動生成をする
  • (自然言語の問題文を形式言語に落とす変換器を書く)
    • これは完全に別プロジェクトぽいけどいつかはできてほしい

標準 Python と非互換な書き方をしていたらコンパイルエラーにする

  • lambda 式の中で参照されている変数を書き換えるやつ
  • 関数の引数として受けとったリストを書き換えるやつ

現状だと以下はすべて正しく動かない。そもそもそんな書き方をするのが悪いので、コンパイル時にエラーにしてしまいたい

c = 0
f = lambda x: x + c
c = 1
f(0) # => 1
f = lambda x: x + 1
f = lambda x: f(f(x))
f(0) # => 停止しない
def f(xs):
    xs.append(0)
xs = []
f(xs)
xs # => [0]

Hackage への upload

アカウントの activation はしてもらった

  • メールで教えてもらった箇所 (依存に bounds を足す + .cabal は checkin した方がいい) を直す
  • upload する
  • 追加: GitHub Actions から upload する

等式を解くことを試みる

  • 一般
    • 構文木として a と b が等しいなら a == btrue にする
  • 整数について
    • 右辺を 0 にする。つまり a == ba - b == 0 にする
      • 両辺に分かれていたものがひとつの式になれば、別の最適化器 Jikka.Core.Convert.ArithmeticalExpr が頑張ってくれます
    • 組み込み関数 f であって単射なものについて f(a) - f(b) == 0a - b == 0 にする
  • リストについて
    • nil == cons(x, xs)false
    • cons(x, xs) == cons(y, ys)x == y && xs == ys に展開する
  • 真偽値について
    • a == truea
    • a == falsenot a

Report the time complexity

テストにも使えるのであるとよい
再帰があると見積り不可能だけど、どうしよう

設計について

現状 (v3.1.0)

  1. ML 風言語の構文解析をする
    • lex + yacc で教科書通りにやった
  2. 構文木を変形し最適化をする
    • 定数畳み込み (x + 0x とか if true then x else yx とか)
    • 大型演算子の分配 (sum (fun i -> A i + B i)sum (fun i -> A i) + sum (fun j -> B j) とか)
    • 大型演算子の消去 (max l r (fun i -> A i)l が定数のときに累積和に書き直すとか)
      • このとき toplevel に新しい関数を追加することも
  3. C++ に変換する
    • C++ は高級言語なのでいい感じにやるとできる

Slope Trick

slope trick の C++ 実装であってライセンスがはっきりしたもの (Apache 2.0 あるいはできれば CC0) を探しています。持ってる人がいればください

C++ コードの最適化をする

不要な copy がたくさん挟まるせいで計算量が上がってしまっている

具体的には core の SetAt 関数がやばい。現在は Jikka.CPlusPlus.Convert.FromCore で core から C++ への変換と諸々の inlining を同時に雑にやってしまっているが、できるだけ多くの情報を残したまま C++ に変換してさらにそこでもう一度いくらかの最適化をかけるようにする必要がありそう。「この変数が参照されるのはここが最後だから move してしまってよい」みたいなのを判断して最適化に使ったりしたい。

変な型の比較をコンパイルエラーにする

現状だとたとえば以下のようなコードが書けてしまう。そして奥の方で分かりにくいエラーになるはず。これをちゃんと明示的に検出して分かりやすいエラーとして報告するようにしたい。

(lambda x: abs(x)) == (lambda y: y + 3)
(lambda x: abs(x)) < (lambda y: y + 3)
min(lambda x: abs(x)), (lambda y: y + 3))

stack build fails on macOS because of cabal's bug

Summary

The same with haskell/cabal#4739

Steps to reproduce

Just execute the following on the environment mentioned below

stack build

environments:

$ stack --version
Version 2.7.1, Git revision 8afe0c2932716b0441cf4440d6942c59568b6b19 x86_64 hpack-0.34.4

macOS Catalina Version 10.15.5

Expected behavior

build succeeds

Actual behavior

$ stack build
Jikka> configure (lib + exe)
Configuring Jikka-5.0.5.0...
Jikka> build (lib + exe)
Preprocessing library for Jikka-5.0.5.0..
Building library for Jikka-5.0.5.0..
Preprocessing executable 'jikka' for Jikka-5.0.5.0..
Building executable 'jikka' for Jikka-5.0.5.0..

/Users/koki/programming/others/Jikka/<built-in>:15:10: error:
     error: non-portable path to file '".stack-work/dist/x86_64-osx/Cabal-3.2.1.0/build/Jikka/autogen/cabal_macros.h"'; specified path differs in case from file name on disk [-Werror,-Wnonportable-include-path]
#include ".stack-work/dist/x86_64-osx/Cabal-3.2.1.0/build/jikka/autogen/cabal_macros.h"
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         ".stack-work/dist/x86_64-osx/Cabal-3.2.1.0/build/Jikka/autogen/cabal_macros.h"
1 error generated.

/Users/koki/programming/others/Jikka/.stack-work/dist/x86_64-osx/Cabal-3.2.1.0/build/jikka/autogen/Paths_Jikka.hs:1:1: error:
    `gcc' failed in phase `C pre-processor'. (Exit code: 1)
  |
1 | {-# LANGUAGE CPP #-}
  | ^


--  While building package Jikka-5.0.5.0 (scroll up to its section to see the error) using:
      /Users/koki/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_mPHDZzAJ_3.2.1.0_ghc-8.10.4 --builddir=.stack-work/dist/x86_64-osx/Cabal-3.2.1.0 build lib:Jikka exe:jikka --ghc-options " -fdiagnostics-color=always"
    Process exited with code: ExitFailure 1

配るもらう変換

配る DP ともらう DP とを相互に変換する処理を書く。優先順位が高いのは配る形からもらう形へだけど、逆向きもあるとよい。普通のセグ木とか CHT とかだともらう形がうれしくて、双対セグ木だと配る形がうれしいので

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.