diff --git a/go.mod b/go.mod index 03fe9ebc..eb00521f 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,14 @@ go 1.12 require ( github.com/DataDog/zstd v1.4.4 // indirect - github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56 // indirect github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7 github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154 github.com/briandowns/spinner v1.7.0 github.com/cavaliercoder/grab v2.0.0+incompatible - github.com/creack/pty v1.1.9 // indirect github.com/crillab/gophersat v1.1.7 github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23 + github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd github.com/ghodss/yaml v1.0.0 - github.com/go-yaml/yaml v2.1.0+incompatible // indirect github.com/hashicorp/go-version v1.2.0 github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3 github.com/klauspost/pgzip v1.2.1 @@ -29,7 +27,6 @@ require ( github.com/pelletier/go-toml v1.6.0 // indirect github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f github.com/pkg/errors v0.8.1 - github.com/rogpeppe/go-internal v1.5.1 // indirect github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 // indirect @@ -46,6 +43,5 @@ require ( golang.org/x/tools v0.0.0-20200102200121-6de373a2766c // indirect golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect gopkg.in/yaml.v2 v2.2.7 - mvdan.cc/sh v2.6.4+incompatible // indirect mvdan.cc/sh/v3 v3.0.0-beta1 ) diff --git a/go.sum b/go.sum index e29b47fd..0c447d04 100644 --- a/go.sum +++ b/go.sum @@ -9,15 +9,9 @@ github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6 github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56 h1:XCZM9J5KqLsr5NqtrZuXiD3X5fe5IfgU7IIUZzpeFBk= -github.com/MottainaiCI/simplestreams-builder v0.0.0-20190710131531-efb382161f56/go.mod h1:+Gbv6dg6TPHWq4oDjZY1vn978PLCEZ2hOu8kvn+S7t4= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Sabayon/pkgs-checker v0.4.1 h1:NImZhA5Z9souyr9Ff3nDzP0Bs9SGuDLxRzduVqci3dQ= -github.com/Sabayon/pkgs-checker v0.4.1/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= -github.com/Sabayon/pkgs-checker v0.4.2-0.20191225230938-5ebdcd474e55 h1:rblyWFJ61sx69WwRsHK/BHcBlFm5X38sQmJnzVhyUoE= -github.com/Sabayon/pkgs-checker v0.4.2-0.20191225230938-5ebdcd474e55/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7 h1:Vf80sSLu1ZWjjMmUKhw0FqM43lEOvT8O5B22NaHB6AQ= github.com/Sabayon/pkgs-checker v0.4.2-0.20200101193228-1d500105afb7/go.mod h1:GFGM6ZzSE5owdGgjLnulj0+Vt9UTd5LFGmB2AOVPYrE= github.com/Sereal/Sereal v0.0.0-20181211220259-509a78ddbda3 h1:Xu7z47ZiE/J+sKXHZMGxEor/oY2q6dq51fkO0JqdSwY= @@ -59,7 +53,6 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crillab/gophersat v1.1.7 h1:f2Phe0W9jGyN1OefygKdcTdNM99q/goSjbWrFRjZGWc= github.com/crillab/gophersat v1.1.7/go.mod h1:S91tHga1PCZzYhCkStwZAhvp1rCc+zqtSi55I+vDWGc= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= @@ -82,6 +75,8 @@ github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241 h1:+ebE/ github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd h1:JWEotl+g5uCCn37eVAYLF3UjBqO5HJ0ezZ5Zgnsdoqc= +github.com/ecooper/qlearning v0.0.0-20160612200101-3075011a69fd/go.mod h1:y0+kb0ORo7mC8lQbUzC4oa7ufu565J6SyUgWd39Z1Ic= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -94,8 +89,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= @@ -152,6 +145,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= @@ -204,7 +198,9 @@ github.com/opencontainers/runtime-spec v1.0.1 h1:wY4pOY8fBdSIvs9+IDHC55thBuEulhz github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc= github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 h1:+OLn68pqasWca0z5ryit9KGfp3sUsW4Lqg32iRMJyzs= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= @@ -231,7 +227,6 @@ github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.5.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rootless-containers/proto v0.1.0 h1:gS1JOMEtk1YDYHCzBAf/url+olMJbac7MTrgSeP6zh4= github.com/rootless-containers/proto v0.1.0/go.mod h1:vgkUFZbQd0gcE/K/ZwtE4MYjZPu0UNHLXIQxhyqAFh8= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= @@ -266,8 +261,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4= github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= github.com/stevenle/topsort v0.0.0-20130922064739-8130c1d7596b h1:wJSBFlabo96ySlmSX0a02WAPyGxagzTo9c5sk3sHP3E= @@ -326,8 +319,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 h1:ZC1Xn5A1nlpSmQCIva4bZ3ob3lmhYIefc+GU+DLg1Ow= -golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -336,7 +327,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -372,8 +362,6 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190913121621-c3b328c6e5a7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM= -golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200102141924-c96a22e43c9c h1:OYFUffxXPezb7BVTx9AaD4Vl0qtxmklBIkwCKH1YwDY= golang.org/x/sys v0.0.0-20200102141924-c96a22e43c9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -419,8 +407,6 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw= @@ -429,7 +415,5 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= mvdan.cc/editorconfig v0.1.1-0.20191109213504-890940e3f00e/go.mod h1:Ge4atmRUYqueGppvJ7JNrtqpqokoJEFxYbP0Z+WeKS8= -mvdan.cc/sh v2.6.4+incompatible h1:eD6tDeh0pw+/TOTI1BBEryZ02rD2nMcFsgcvde7jffM= -mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8= mvdan.cc/sh/v3 v3.0.0-beta1 h1:UqiwBEXEPzelaGxuvixaOtzc7WzKtrElePJ8HqvW7K8= mvdan.cc/sh/v3 v3.0.0-beta1/go.mod h1:rBIndNJFYPp8xSppiZcGIk6B5d1g3OEARxEaXjPxwVI= diff --git a/pkg/solver/resolver.go b/pkg/solver/resolver.go index f528c363..f26f8282 100644 --- a/pkg/solver/resolver.go +++ b/pkg/solver/resolver.go @@ -16,20 +16,253 @@ package solver import ( + "fmt" + "github.com/crillab/gophersat/bf" + "github.com/ecooper/qlearning" + pkg "github.com/mudler/luet/pkg/package" "github.com/pkg/errors" ) +const ( + Solved = 1 + NoSolution = iota + Going = iota +) + //. "github.com/mudler/luet/pkg/logger" // PackageResolver assists PackageSolver on unsat cases type PackageResolver interface { - Solve(bf.Formula, PackageSolver) (map[string]bool, error) + Solve(bf.Formula, PackageSolver) (PackagesAssertions, error) } type DummyPackageResolver struct { } -func (*DummyPackageResolver) Solve(bf.Formula, PackageSolver) (map[string]bool, error) { - return map[string]bool{}, errors.New("Could not satisfy the constraints. Try again by removing deps ") +func (*DummyPackageResolver) Solve(bf.Formula, PackageSolver) (PackagesAssertions, error) { + return nil, errors.New("Could not satisfy the constraints. Try again by removing deps ") +} + +type QLearningResolver struct { + Attempts int + + ToAttempt int + Attempted map[string]bool + Correct []string + + Solver PackageSolver + Formula bf.Formula + + Targets []pkg.Package + Current []pkg.Package + + Agent *qlearning.SimpleAgent + + debug bool +} + +func (resolver *QLearningResolver) Solve(f bf.Formula, s PackageSolver) (PackagesAssertions, error) { + resolver.Solver = s + + s.SetResolver(&DummyPackageResolver{}) // Set dummy. Otherwise the attempts will run again a QLearning instance. + defer s.SetResolver(resolver) // Set back ourselves as resolver + + resolver.Formula = f + // Our agent has a learning rate of 0.7 and discount of 1.0. + resolver.Agent = qlearning.NewSimpleAgent(0.7, 1.0) // FIXME: Remove hardcoded values + resolver.ToAttempt = len(resolver.Solver.(*Solver).Wanted) - 1 // TODO: type assertions must go away + + resolver.Targets = resolver.Solver.(*Solver).Wanted + + fmt.Println("Targets", resolver.Targets) + + resolver.Attempts = 99 + resolver.Attempted = make(map[string]bool, len(resolver.Targets)) + + resolver.Correct = make([]string, len(resolver.Targets), len(resolver.Targets)) + resolver.debug = true + for resolver.IsComplete() == Going { + // Pick the next move, which is going to be a letter choice. + action := qlearning.Next(resolver.Agent, resolver) + + // Whatever that choice is, let's update our model for its + // impact. If the package chosen makes the formula sat, + // then this action will be positive. Otherwise, it will be + // negative. + resolver.Agent.Learn(action, resolver) + + // Reward doesn't change state so we can check what the + // reward would be for this action, and report how the + // env changed. + if resolver.Reward(action) > 0.0 { + resolver.Log("%s was correct", action.Action.String()) + } else { + resolver.Log("%s was incorrect", action.Action.String()) + } + } + + // If we get good result, take it + if resolver.IsComplete() == Solved { + resolver.Log("Victory!") + resolver.Log("removals needed: ", resolver.Correct) + p := []pkg.Package{} + fmt.Println("Targets", resolver.Targets) + // Strip from targets the ones that the agent removed + TARGET: + for _, pack := range resolver.Targets { + for _, w := range resolver.Correct { + if pack.String() == w { + fmt.Println("Skipping", pack.String()) + continue TARGET + } + + } + fmt.Println("Appending", pack.String()) + + p = append(p, pack) + } + fmt.Println("Installing") + for _, pack := range p { + fmt.Println(pack.String()) + } + resolver.Solver.(*Solver).Wanted = p + return resolver.Solver.Solve() + } else { + resolver.Log("Resolver couldn't find a solution!") + return nil, errors.New("QLearning resolver failed ") + } + +} + +// Returns the current state. +func (resolver *QLearningResolver) IsComplete() int { + if resolver.Attempts < 1 { + resolver.Log("Attempts finished!") + return NoSolution + } + + if resolver.ToAttempt > 0 { + resolver.Log("We must continue!") + return Going + } + + resolver.Log("we solved it!") + return Solved +} + +// Choose applies a pack attempt, returning +// true if the formula returns sat. +// +// Choose updates the resolver's state. +func (resolver *QLearningResolver) Choose(pack string) bool { + resolver.Attempted[pack] = true + + s, _ := resolver.Solver.(*Solver) + + var filtered []pkg.Package + var index int + + //Filter by fingerprint + for i, p := range s.Wanted { + if p.String() != pack { + index = i + filtered = append(filtered, p) + } + } + + resolver.Solver.(*Solver).Wanted = filtered + //resolver.Current = filtered + + _, err := resolver.Solver.Solve() + //resolver.Solver.(*Solver).Wanted = resolver.Targets + + //resolver.Solver.(*Solver).Wanted = resolver.Targets + + if err == nil { + resolver.Correct[index] = pack + resolver.ToAttempt-- + } else { + resolver.Attempts-- + return false + } + + return true +} + +// Reward returns a score for a given qlearning.StateAction. Reward is a +// member of the qlearning.Rewarder interface. If the choice will make sat the formula, a positive score is returned. +// Otherwise, a static -1000 is returned. +func (resolver *QLearningResolver) Reward(action *qlearning.StateAction) float32 { + choice := action.Action.String() + + var filtered []pkg.Package + + //Filter by fingerprint + for _, p := range resolver.Targets { + if p.String() != choice { + filtered = append(filtered, p) + } + } + + resolver.Solver.(*Solver).Wanted = filtered + //resolver.Current = filtered + _, err := resolver.Solver.Solve() + //resolver.Solver.(*Solver).Wanted = resolver.Targets + + if err == nil { + return 24.0 / float32(len(resolver.Attempted)) + + } + + return -1000 +} + +// Next creates a new slice of qlearning.Action instances. A possible +// action is created for each package that could be removed from the formula's target +func (resolver *QLearningResolver) Next() []qlearning.Action { + actions := make([]qlearning.Action, 0, len(resolver.Targets)-1) + + for _, pack := range resolver.Targets { + attempted := resolver.Attempted[pack.String()] + if !attempted { + actions = append(actions, &Choice{Package: pack.String()}) + } + } + fmt.Println("ACTIONS", actions) + fmt.Println("_______") + return actions +} + +// Log is a wrapper of fmt.Printf. If Game.debug is true, Log will print +// to stdout. +func (resolver *QLearningResolver) Log(msg string, args ...interface{}) { + if resolver.debug { + logMsg := fmt.Sprintf("(%d moves, %d remaining attempts) %s\n", len(resolver.Attempted), resolver.Attempts, msg) + fmt.Printf(logMsg, args...) + } +} + +// String returns a consistent hash for the current env state to be +// used in a qlearning.Agent. +func (resolver *QLearningResolver) String() string { + return fmt.Sprintf("%s", resolver.Correct) +} + +// Choice implements qlearning.Action for a package choice for removal from wanted targets +type Choice struct { + Package string +} + +// String returns the character for the current action. +func (choice *Choice) String() string { + return choice.Package +} + +// Apply updates the state of the solver for the package choice. +func (choice *Choice) Apply(state qlearning.State) qlearning.State { + resolver := state.(*QLearningResolver) + resolver.Choose(choice.Package) + + return resolver } diff --git a/pkg/solver/resolver_test.go b/pkg/solver/resolver_test.go new file mode 100644 index 00000000..7ca94116 --- /dev/null +++ b/pkg/solver/resolver_test.go @@ -0,0 +1,150 @@ +// Copyright © 2019 Ettore Di Giacinto +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, see . + +package solver_test + +import ( + pkg "github.com/mudler/luet/pkg/package" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + . "github.com/mudler/luet/pkg/solver" +) + +var _ = Describe("Resolver", func() { + + db := pkg.NewInMemoryDatabase(false) + dbInstalled := pkg.NewInMemoryDatabase(false) + dbDefinitions := pkg.NewInMemoryDatabase(false) + s := NewSolver(dbInstalled, dbDefinitions, db) + + BeforeEach(func() { + db = pkg.NewInMemoryDatabase(false) + dbInstalled = pkg.NewInMemoryDatabase(false) + dbDefinitions = pkg.NewInMemoryDatabase(false) + s = NewSolver(dbInstalled, dbDefinitions, db) + }) + + Context("Conflict set", func() { + Context("DummyPackageResolver", func() { + It("is unsolvable - as we something we ask to install conflict with system stuff", func() { + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}) + + for _, p := range []pkg.Package{A, B, C} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{C} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + solution, err := s.Install([]pkg.Package{A}) + Expect(len(solution)).To(Equal(0)) + Expect(err).To(HaveOccurred()) + }) + }) + Context("QLearningResolver", func() { + It("will find out that we can install D by ignoring A", func() { + s.SetResolver(&QLearningResolver{}) + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}) + D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{C} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + solution, err := s.Install([]pkg.Package{A, D}) + Expect(err).ToNot(HaveOccurred()) + + Expect(len(solution)).To(Equal(4)) + + Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: false})) + Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: false})) + Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true})) + Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true})) + }) + PIt("will find out that we can install D by ignoring A", func() { + s.SetResolver(&QLearningResolver{}) + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}) + D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + E := pkg.NewPackage("E", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}) + F := pkg.NewPackage("F", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + + for _, p := range []pkg.Package{A, B, C, D, E, F} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{C} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + solution, err := s.Install([]pkg.Package{A, D, E, F}) // D and F should go as they have no deps. A/E should be filtered by QLearn + Expect(err).ToNot(HaveOccurred()) + + Expect(len(solution)).To(Equal(6)) + + Expect(solution).To(ContainElement(PackageAssert{Package: A, Value: false})) + Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: false})) + Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true})) + Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true})) + Expect(solution).To(ContainElement(PackageAssert{Package: E, Value: true})) + Expect(solution).To(ContainElement(PackageAssert{Package: F, Value: true})) + + }) + }) + + Context("DummyPackageResolver", func() { + It("cannot find a solution", func() { + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{C}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{B}, []*pkg.DefaultPackage{}) + D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{C} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + solution, err := s.Install([]pkg.Package{A, D}) + Expect(err).To(HaveOccurred()) + + Expect(len(solution)).To(Equal(0)) + }) + + }) + }) + +}) diff --git a/pkg/solver/solver.go b/pkg/solver/solver.go index 583d3758..3abf5846 100644 --- a/pkg/solver/solver.go +++ b/pkg/solver/solver.go @@ -35,6 +35,8 @@ type PackageSolver interface { Upgrade() ([]pkg.Package, PackagesAssertions, error) SetResolver(PackageResolver) + + Solve() (PackagesAssertions, error) } // Solver is the default solver for luet @@ -230,6 +232,7 @@ func (s *Solver) Upgrade() ([]pkg.Package, PackagesAssertions, error) { } s2 := NewSolver(installedcopy, s.DefinitionDatabase, pkg.NewInMemoryDatabase(false)) + s2.SetResolver(s.Resolver) // Then try to uninstall the versions in the system, and store that tree for _, p := range toUninstall { r, err := s.Uninstall(p) @@ -381,7 +384,7 @@ func (s *Solver) Solve() (PackagesAssertions, error) { model, _, err = s.solve(f) if err != nil && s.Resolver != nil { - model, err = s.Resolver.Solve(f, s) + return s.Resolver.Solve(f, s) } if err != nil {