From 13261659517cf76f6b22c4d573b02f0ea3f695a8 Mon Sep 17 00:00:00 2001 From: Hannes Weichelt Date: Mon, 7 Oct 2024 13:05:30 +0200 Subject: [PATCH] Removed code unrelated to API --- README.md | 93 +-- example_mus.png | Bin 56869 -> 0 bytes example_show_decisions.png | Bin 78585 -> 0 bytes pyproject.toml | 6 - src/clingexplaid/__main__.py | 27 - src/clingexplaid/cli/__init__.py | 0 src/clingexplaid/cli/clingo_app.py | 340 ---------- src/clingexplaid/cli/textual_gui.py | 876 -------------------------- src/clingexplaid/cli/textual_style.py | 167 ----- 9 files changed, 1 insertion(+), 1508 deletions(-) delete mode 100644 example_mus.png delete mode 100644 example_show_decisions.png delete mode 100644 src/clingexplaid/__main__.py delete mode 100644 src/clingexplaid/cli/__init__.py delete mode 100644 src/clingexplaid/cli/clingo_app.py delete mode 100644 src/clingexplaid/cli/textual_gui.py delete mode 100644 src/clingexplaid/cli/textual_style.py diff --git a/README.md b/README.md index dac396a..7f04a65 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # clingexplaid -Tools to aid the development of explanation systems using clingo +API to aid the development of explanation systems using clingo ## Installation @@ -19,97 +19,6 @@ pip install clingexplaid Please refer to [DEVELOPEMENT](DEVELOPMENT.md) -## Usage - -Run the following for basic usage information: - -```bash -clingexplaid -h -``` - -### Interactive Mode - -We provide an interactive terminal user interface (textual) where all modes are -accessible in an interactive format. You can start this mode by using the -command below. - -```bash -clingexplaid --interactive -``` - -#### Example: MUS Sudoku - -Below is one Example call using our [Sudoku Example](examples/sudoku). - -```bash -clingexplaid examples/sudoku/encoding.lp examples/sudoku/instance.lp --interactive -``` - -![](example_mus.png) - -#### Example: Show Decisions - -This Example shows the interactive Solver Decision Tree generated from -[`examples/misc/sat_simple.lp`](examples/misc/sat_simple.lp). - -![](example_show_decisions.png) - -### Clingo Application Class - -The clingexplaid CLI (based on the `clingo.Application` class) extends clingo -with `` and ``. - -```bash -clingexplaid -``` - -- ``: specifies which Clingexplaid method is used (Required) - - Options: - - `--muc`: - - Computes the Minimal Unsatisfiable Cores (MUCs) of the provided - unsatisfiable program - - `--unsat-constraints`: - - Computes the Unsatisfiable Constraints of the unsatisfiable program - provided. - - `--show-decisions`: - - Visualizes the decision process of clasp -- ``: Additional options for the different methods - - For `--muc`: - - `-a`, `--assumption-signature`: limits which facts of the current program - are converted to choices/assumptions for finding the MUCs (Default: all - facts are converted) - - For `--show-decisions`: - - `--decision-signature`: limits which decisions are shown in the - visualization (Default: all atom's decisions are shown) - -### Examples - -Given the simple program below [`simple.lp`](examples/misc/simple.lp) we want -to find the contained MUC (Minimal Unsatisfiable Core). - -``` -a(1..5). -b(5..10). - -:- a(X), b(X). -``` - -For this we can call `clingexplaid` the following way: - -```bash -clingexplaid examples/misc/simple.lp --muc 0 -``` - -This converts all facts of the program to choices and assumptions and returns -the contained MUC from that. - -``` -MUC 1 -b(5) a(5) -``` - -A selection of more examples can be found [here](examples) - ## API Here are two example for using `clingexplaid`'s API. diff --git a/example_mus.png b/example_mus.png deleted file mode 100644 index 43524fb1439bbf65df50f517403f33e7cfb400f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56869 zcmeFZWl&s8+ct`mKmr7UBxn){?(PuW-Q6v?%Rqnt!4uqFXK>fSg1b8tAh^2@e8b*5 z`+2Iqdh5JD&aYEvYQfZ6t5^5!cVBm}o=`=3iI-?^(cs|VUP?)dD#O8{Ou)e*;Xgxx zT`?Aa#D#q!Ie(H;eFppZJTv_UJAdOMrs<+$Z|>r5Ruj!f0c)(lS6o{WWwOxo2B_Qk?R#>~RQ&cy?}A|j@u3g``# zhJz!6lM)qH^+?-acK4Z{hW4J!wsLYlH$uU}ilbgzc^_6C6{c>3OVoghSE?SKpsiz} z2Q993RV`j*qw4t_D->>?Rz&KOA=+AR~cvA zSKin#F;nN==&ie_n(rYpnlzt) zrU*5=56uNzA1BNIZSpocC7^Tw5>0X4nzB%)lBJ%=1gwMC&PwUE_Oi`^%KTxc_(akL z)B{AQ(R>LhFN;nAYvkWPo3KXf1Kw&+n}l9c{7X|0HlXjHE@P(HRO^XNNlh>d9O-pfbq0by^ zZq(6SD&6?HM*n$;ym*zOI^U#)iGCoVL#w5C4cR8Xt43RfO%lY=AWbfc-qT2WR4J-n zq^9w&dSl*ZrhKXys3%KBwNM(U${vVTn>F|X6si3hP`6MfDZ?fTB1%C9H5#a7;gv%? z{?$D>bt_Qkhq}slyvVa!V=BDTDRDJ_Z8;&oZ3R64 zY9Nk;9og!nv&I8{p$Dtfn5>RjHuv7<`$CWvd$5gbtGM(Gy zLKgiPMjtRIQ}n-Wn!M^3wC2nKN9(WFuKJ7BkU6!pCM;W9?Lc&$PrcA4pMp?Pt^RE( z(3VJKdg%!H#Ylhu&LkLcH#3~d31KF};6xAlw7P&H`NxE$W&ZNAU|Tw3)K zdMc`G3=fQ?m!fm83)EZ%SAcZ`AP9|bsgEBzjEQO6y`(<%sB9XrIl>mF&CGtE-Ez~# z(9qD50CuRNvhpW7ybo_9vBRf%i2h?(IM~mSo}mOHA;5R8GG!}%>+J08M#pDJQ_P0bXX+#gn~2x}cH)vTgw zv&)W?p^=f1vY7NAN~{*uw6&!{>ZNs095vL`xX@GtU(=2m$CH#>$7_GmQWJMFvl|y~ zp(mk5{Y=j`4ttUu``z2fne39E$O!N(EG&I}MT3iE|2UEIY03ci zNr7@tu( z`LykUhiajG`Yrro_g;yQz05+Lt&F(1cZpk@+~F^?=mLRBQr*q3 z=f&AK^c<%tdP+5gPRSOJdX-J!HbyWWGSlGw~jLXmTWNXXE!u(9n9KPgIQpGAXCoucg5V*we$1yu+!7i({G~hFBH=+D2fVjtq9e8=!(zF3ciC1;>rr>NmODk z|2$2eFHMG3%qLQt;Fq z60gIr_DQa)D{XP;4R=98LGzQ-s8JAMIG^@YIu9}Tjx4<6jA@abPf7dBY&FXH;|^^SuEvAJo(xq z5BIl%I7!#Hkwo#SIfCV%__-dc1{;6XAa4KeC57dZx22Z{i)Y7OnAJz%yVJf8(GI)C zg-)#O7 zyI1B>T!AV_j|sPp3+1Fsm z+zf*`Jx@(PDr5|CZ>4qVxi5E{CF&(y##^oTM9OS?N|1P(w$3QU!lH?nmGi+uEwv5p z^jjd32>WY{-Km zf~Y2&A0SW-N9TcNHuDkD+f06|ODotC_RwNjtb#ytzD%Oss~olSA=c4~MZ4yc>P%>a zh5?`!9<-?3gjgkt6ewI(1-xz7JuovjS0{W;0W7$M5!GsK;5VftkzWd9q;J_BGTeLw z9$d6=H|RKrp%O(ssHxK^_p{RfFC)4uQTiud(lBu;Q-e~CUM9gu+g%6;9#e7_zsBkOHW zAfm5Xxl;~Q)WP7odt3I_=XJ9kKDqs&MaxCBx2MCDE(cQcaV#j(n&HC15u-?Jg9_N1X==#<13F`0pJg_>bv} zBMXru2imVzjuBZY!R-ciesQ6+yO+JyFc)~+Q`t*0*o7&myMCtcb0QY};!VsYSt)hj z&`?RD8=^m4a&j^(v`-hOi|&GG=s*mnHs5Y@#;Httqc#?7%3&^f2K$0%GHiz{YutQ$ z*!K896IjJFrLinsBU%M~t5HPJRBK|}S>e^lpv^^8#vuxN%Ig=G(Vw0~Sy#q=_i(Eu zuDg_)vqO6F&_J+^$rFJ7SdO0zLUG#qO}y=Q>PR%_OAqm5i@NFNu1RbChU;Q@^Z~29*3bXa~ot>5QqsElOy;dJB+ z-yT{)y)Zepee7LGE<77^e?=y)OAOEw)nD}O1b^-z8`S9-UqdCukrDn4{B$f9-&u{ z1SQidfZE|o>!r;~j#)`iS=Ydo3iy2~4b1;+um{DTCaJc>0mSmZPpj{+GR|IT=$#Ni za78Y`8Ye$n3mqmHZ%-5ZgUl4@ZsH`$har)Kc-(4KY^*n;}YJl&h1xpfbWY+Q{4@&L@B085 zrug(VS;1f!SpoH#nFS68=}%IK*O$yAcQOEr_=y#Nn;=Xz)?EHVfoZ|zN-7QF+DrtW zr)=$CUPKac#|V+Nv(M@xwzpobGj@ky>S|t)9No#RfVC?1IjeV?t849J= z8Y|Gv=t4Nro4$Al8IEzv{VrFnLOk zs*eFk{u5RTXU%t%6=j{7MwM!lS~xO3HtCX9)MF8(f?wW*Dx#SNV?T z4=mpVuL|Z)?{hY69sU@i+3ilQf3aRPWuT!4liG#pP!e;R@9hpdid7)WOXKtH@t3M< zlj_s#O>boubwmKT<)L+sHoal9vY?RLUG{L`5!uUKPdGvm)_S+6x%<@j_tjuCFR2Jb z_od_mJV-BZHAfua!|V62L$RJ0p1s8t)^OO( zHNmo!2jRhMZeYXUQ(n(zB9*(ws+DlPxkr@6wz{`4eGMa_D-=$>!iHr$UZh ze0g`&dZk|r5!@}xwO)36uRJaesMAv+L_~S<&0DS}5=JV6EXWLS0nP$(2y4IB;b@A_ zg^`+9s_n9qdVh9tHg9ie5d3&2Cx3m)t1hZjC30k}FW^TtN~d7sT9Uz>zVQQm{?-T8 zZ#cbgDd1o`RcBS9b{iXyE(~kWgGoc+Tt{8slk=#QNfrhct!1U9r5j+mtYNRLdCsQt7or;kH#w=T4ciGjb76o4TRpogYW>2Qm5$~y75rF^o5>2P7Ib${8CyW_s# zN%V@=csYz)_Qw3};CmsLSC6e*jQh^(Vnj2gh;NYp%4w0~VYsvn`&ENnJ9)7Uzhh*! z=c_5}_lKRYgh;OR3`{3gvx0Ipp&T7AT+PQ!hI)~e0N zW1-Ou{;MbrAtR%Add+TfMRNMzndZS#*06?#f%tnQB;|>O3kvhg{dtuywEkE4Wif z>PtyIhoK--3yZ?M)|{N2xFA(S0Ch~&5KR&=m`3uE3lJ32D&&6XF!Ao6XB5TJOBDxgM_PJM%LQ88!Sa>3^N zKhN7j|3-cmG%(sL+zzw@I>=yY7B;I6==c@QF8cmeSa|rP+e@B3namngD;UgsNyzaX zHm987dAhOC_D?@nd;cO27|OH;bp{&)UNRocN)Gn*kqHV4E}OvwqaZH6;4JnJlN0JC ze}gjVi&7;dE5gM4{K17<8)|y`3(_Z-emaWv|A9osVD_;2L>N{O7KVQU9<=RyR!x-uX9(rViNE1c{_PZue;5oB7f1Oa`4v{?zzXmF)@^*T zU_pjOhV={!EeHldk4ba>j4RCcHxw%tU=Jg+&4pn;s@Kx)Z~iELy1BqfzNL7IdOD=W z;KRQq2r27`7ynUA9@qc!gyk(THy|J&c=undF9obdL7zxq&|6T{`-d0U(5^Fv!f%CU z%k&7Vc>lf1?sT>_s^I=y?ewkHAM&J>iC#9n{|{L}c^Kb?nZ7*GyDQXRftUin@)PxQ zYHDg$ZLE$-{w-4F(Yzqy`ko2hw-9`|>ZiK zNPafsr>8;|1YWK{hYeImofPzYXF4KT#(7e^&-?b{Mi4sutbl-Jb)qEW6iu>MIS87paRxV`|tJPa1J1 z^ggA-GMeD>U&u3td|C?TvXgqB0v-Pb`hOYWYl&F>9|fg4M;h{S?$2QX-?XQ=Jg*t) zubZ&MqWhnc2YL7Z>@)vgyYmzSU>-2#A=|{fj4Qb?bKq^$MEjJi^5p*aB2lBX!DR6L z-PZE(Q|;X5jit{)vE76#Um9vm-KQIn;i)o}b>6?1p89rSl>|}Eh5GwFVCEVCA|U&z zS`UU8A$n)zxes^thkRwY#atW2hwkj!z5 z>!%i;TmA3$)w3@n5X(0<+(}l`4lj7J$iL2=RIqYyeO_Ga}=`I7C zd|b51?0F5XyFRQW@=a}@KNH%!50{7pDtX*$ah(Wzn0F^Ybn(LH*$u zkaHEPR%=PsaWa!izWG<7kRA7>8&7TjH{2>ys|duqi+9cw8m}#mq}y6gwOWSitwC6{ zru{JlsF#q)p;1buv{roRWxvTujN3`3!8I~wnjbc%inJ9uGPJO%l}1eKz74g}Q`|WK zX~*FjE0{zx6UzQk*lk-d$+?Mdut5p(U1Rw!ZEbx#=MgJ7y@g*TvnBP_8CocIuI0%+ zy+V@RhcmLNjjbhzEybo`kMzRB^iJS@I@je8Ys1bZ)2!|T!DPzR^E&d)=^2I={yXAY z%ybr@%xja^^!EXdDv6v??S?i9t%Qhv0GeiVH9x)?g$K^7Hs;s~@|2OGFFVff{1hyc zQ4Pw1(}w7~Fun-koqvAm9G>Mp_&XR&0k{Z~cXQsJCwVUL-8nTNUaF<%V;r2PG-<#SXCe$`k zQfObbm;q?%w9teelu5OdhAPZ+AsDlqi_>sc(8lVk_adpC zQbzl%ygBOEx0OfZ1I8GJ><})^bs14n$5oh@P))LUlILv1WZDaf{ca^C>P(H$!rpH_V zG4_-CXnJQ6CCTsp(7`#jn3P_^T_>I^=-C9?qCCcGsFKLQVGo+e)#hZwSyz<{ZRL8Z zA;2GNo_o0O8Io1To3EPRN;c^&mC&$0Xe*0$1WU$1KRWuD7&T!u1c#%N3TG}+4Ys}M zYJ0e)I-)Nbb}Gj@xFY;XfuAM>$vAk9&tSB0o#?rr`60PdU_Mg>zB5@1NqR;_v1O;0 zut1;(cS}HFXCZR^S!M5rg5#+0I)U4Gt}Lw|*Lu6n&1k0E+H`i?T5=<9PuiFVPPztr zG-tAByhody-YeE#zrFzes6rj{mg*X2G>fRMt{e9Je(6Xb0fG~*Sox+5#FTD963=zg z)k?^&Sfs^d*>msL6kzh6I{ z$1ZwO4MJGJn3*@%1{Rk&uX2@{dnv^U7|Z_zXSI8$!jNAyVC*A42849v0C65SrPQj0 zqT29SM^SN=J^CJpgu1Qm?483AycvMf#V57|Gm8zOQfMBn*8}^I)Bi*{JMyJqdnP1A zL0!0-UG4r3MmI#6_ks^chl7l5dvS~z{G2jrDDh=k$2LQ!EG~`}kKO!B)agWqjc#yjMWE;S$Icu#UFL0)7 zw@0v1B?#`$Ic@pi9o@0yGH)a*==sCDXDG+A8T!U$)8CZUzWVYLzi&sWAA#gZ;pjRL&yqz9;yg2*J_n7J6-J#2a z+tR_F=rc0?!OnP2n3L&u!1EWrzes}9Sb`Nu?lPWX?FxL;dGvXA(B_%!2lb=@r5=s+ z+F=P74P>8X0?zZlEe1fTki)@@v_h>hn8nb;OnFkgMH)&W6f;cI9 ztb@E9*JUK^_e0Ifb-O(M9#bS)p4<$vQNnrVRr%RDwEolw#LsmTP)*<~B7n}|7nUqF z5~x^S-Ec9}@!RK~@!|BEv&5#Au@HJXyEG3@%C3))JKa+JDrVr@5#y||7L%=_c~R~- zG!iXj$`4A=05>VGVZX8OJfrbH`N&$1ec*0`rFTJbq`;r#jA2>b{d`-~g~FgkFrH5k zal-Ju^JAAtxn7@%h1-g;nl>Fv>LcGd&!9Ckbf(uZGwS#Sg^w_5NDAC~L%Z4uFzgRGZkKS_5vXuM;JbQ5 z)?F{xSALjjwgBy$jy;AN{fi z@t@Wjv&XlrB@wvBu+zqH&wy|Kc!sYI=Vgo9v2i?tI#w%qzDVMfB9htkc_*p;DOiO@ zJOwMuwkO0qrAYwuCLwe4i1Lr}+1+EA&K~`}OWd4&?GD|eHNo4?*GA`kq`jn%N8AFm z2-j)-LByV$z%K5VGsz4C0_0EUhwfy=GZE{USFJ-^gm48c_4D$4z9gx&^@Beo#%-Wn z57+H`8wYY0<`t-;x_}oU?gXQ>GW8ia6+3FaK7#iFcpB*)tAstyTl)jO_&Ff+<9-R& zFIcVKnGz)_WRVk6Eyu@_U@cU-w_V@-NC%W=N&3JC4_!+eVaBY+_39eYin8iWj`xNi<*qJF}jiaYnuCIKiVg-u)>il(cQ6WSo4KbUq$J7vX$L zKMs2%3Q!rK;JNQUd;Ez^sL-U$eC}2F%@+ORYV)a@c{lCmeFFk}rEe@BxU!fJ&}-6C z&4c38F-x)RaoHrYUB6yEBuk_8vT|^|cftlr@Zd2Tz9AR_HZymi%(z(|bxY z>oprnqT81r@dy(b%^eUoMC0RKnd7+$>$edd7~eBf&sTcY+E^x_g8bhA*|umbhP%(cdk-mEXopy!a{<HY@pnZ&Q!+0}7mCL;7u1MCUT=uM6%98}>!Do@$7{Lm%I@!}Fy(Y*V&Ce(#PfaD{u?dQ1^4w~Vz& zJGAr9sj1>#V6|{FYW}22LpksX|?sT3VOXMH3lJeSM|zSj~rxFFQ$- zcx|c^COB5U?uM6`k4#MqePj22jZLf9y+o~&q`XJ;uJNSjG@;^mZ@wTiS|WqF>t_pX zN`O)?S?NOEyC&0>O~DYn5fkLrohdM$V2ClY&sFDz?s>-wk=JjdbHN%xi~B=LqT1Qg z`^|FF>H<8A0a(a3KBKuBsyseD$mhvE%Dr(Nt?d2=kT7xqG^93!=^Y>^os0o@21 zEGMk!g#0kKZ@xIM#)sbK?nf}*w(l!N9oLg3vd43oY7H|oy+x58+3RZ&4;S3?fuKX; z)vpYx_A*IjCm)p!$nXzo+veuCXMfqBtv1B20_|##IEznE47U#6y}$}s=KbU#d{Y-j zj-OYmiCIZ@-WSw)3OQ-632c9%nS!bmv&g_QhZdaq{7637-J-)CWHd3$;7AN-96uu4ZTxc$`i5i+ zNxj8c`%QD&;QFJ0%_L$rlUBBxtn}TRj8esM(~K5hr+Mh6-lQKky;R2%fZ%-ajLY}P z?l;FE85Z0pEjIKaId?0mP&$v~MZ1^I{qwz!9mm3KqhI<`v|1j|4XzjWCg(jo+g&0# z$8VMCi%;xMoS%^?4zlLMv$zp=u9q9AW+yReb;Qd8FOo|Mf@D6jZr=1qcs`K2+;!70 zah|iFMBYY76jC%^k(0Bn{EXJI?oBPbi4o`?N@ntJyi1cD9jZFdIbJ-G)!nzQIj8!y zE=c0Y1_F z{;7giYxZ4T=R~0C?K7-zSF;{zI148ujuPgr0~da8dI!S%zW2N@0bK@sSlC2bbY7~h zckt~!xj(pUJDJ856a5m=%DB;Cq_M!`zO%bX~3ptSaNy;!huq`qSF;k_}eU^J{o0V=gJVzSVrbr%uj0 z^O@&xDt4*!#;46v&y$-liSyJJ+;DugU**gjFqwd3wmvY3w59fGUGBUg->Iely!ql= zY4T75BWAXDTOHE-!E4j|*%EtfsqPu-reiY_pB-Zio^slbm?O{I>>sJ42exuZ836Q^ zp};`QU>sgyE;v^3*ItWRlQwu?-5vcC3(cB)-y#lD*}g9(z6cVe@U*7`b&@_u&K~X` z`Iep^u3!f2ytU7{$Zs0ej^M;Q60|<)h!ui+*4d7eZ9lUKv6-RiuM{_2;Oj&0uv;D| zJ4lR0w>Uz#Z$k{%h)15wJ|$!>x-RUDCdBLAsp&cS=&_{Cvx0Q@RMg4q`MJz$O(DLr zz=v?aGrrTSAdAvB5B^}=XR@&%w-{AwP@f^R7=@dGZyIvQ8}nwarL`7x_Kh9A(r8B+ zkDj`jeq=ODFNEW_DO>G@GEk$V3??+Za6zB+N`q08lPWVhow^8i3V)jVqKG(gvN++2 zC>5(=;Pm&!ppjutj@Nm%i6d;`Wk%Brrh+j2-iVPZ^tR1x%q%E&g@<+vA75k=2$nm8`Z1HzmW6)5hu1Kq?!J(swg_ zfiGq7DlL`fZ5{Q$xe+h?h~0lE>_|9o-q(L%*(az29s8vi9=f?alc3VnxL)UOWvA8X z&eZs!R#L)NZcSpJ(_6~1UTUYGZw1cNV?GnO{XS2aQ}TET5U_eNYq3;-dpe){_$$}- zm3rg@uL7Xiwj3h@uVV=Io`2WLUAusu;nj$lu$bD7v3e1mg=T!4r{<41I2TKJe=48q zA|G4>qyjqXy?VePxgUIVM{j1A32A5C%L+Y){bj-tNHv}2jghTZsZ=y4+?$Rn`?B(7 zv-21r<8%y({R?mkdNRU=mqhYe4BTI^P{q& ztHaC=i>b#Dvs99d#=OsmH6zrRV+dx>lvsgBc=38B5!gvhNI9^Fb0)Z~VFuPqKYudA zRzAUI0J9A8Vg}tG>a?#4q94CBQa%KL%JjObk4CbxrpwYiX8e)B-Z7YX5}f=;>!UYD z?aaEK7npq9Zg@clSL@r*>d>EIoHj>yR}hntFPYBMWAS{@2R<|>9_O)9RA=iDg@%V& zR?V0wBfIK=-|IO$bMexrM~Lh|4D*+Rp|NJI4C@c$I1?S7EZdP^&*r5U+-rL zH^mFC&Cl}bmqeL#4EbuKB3uU%{Lg#Tdpw1|;#Qki^4uw+=yQFEusqGK-sv_XIOa+9z zmrw_i@F7HBat@yeO1DdxGo!tb-`R*>jg{o*{hRp9gjVow1TkYu#$4x+tdF0#k0+JT*|JI z(9OQ*b+fb`n*sA<)yT4X=$Ne-A?^XLg6$#bKwr5`lCfLwq|0|Nl2L{cus)+CJ`%Y} zy@&ExDp(3)f*;^~Z}{1CFr8^aXl*4U~RAyg=C3?5r+Sv{yI_p z#SX^#AMlr4$l!j6J+5-%^GmNKy2H%g9>97Zsu?z#(CJ`~HR;E4d%1ZKUQEVsH9Qe|T;3G4UmL{Gc6QU_=0gtoNgU@8cZOj4B3# zHIe;VFMZ=v%u?nzNQuGTj0A)t*S0c!S1$QBdPLllC)rgZuG5UNSu%H8Su0e*{kmrn zt-0)gf(FR~^Gm?hYFGF1uG5^W1wj?%y{prGV$I>pw$VTd%C59{GunUvZgOJm`hEC% zV$8>TBCs|*EsJ5{ii5e|Ts7XTAJ1>91{Om7)aRrCojQbKH5;x=vW6n}8y(&76A~}3 zA^PBY`~+kc<}8VzQ7;NFK5QW4^XC_-`yPl6bJ>b)ZyxH=v}=NJFatB|mQIV=hXa>Z z94L>c5}C|UUI%4)b2eOKapj1`1R`H@Aw5hH>8GJU9^eL9vTWSBvKfgWc9UmLO`Qjb zzfMLCPo*`J;Q?;J&X|Gew1o<=neg~}!oG)1ibiq9Uu*KJkTqyJHf#&f8olN!9?)SB zu5-hHb?`{z{uZvneYPys52=1MyBGc#`gOdPMxYt6rQIrjk^Ao|s^H2IbfDPPP9dh| zBFS-=7HG{QkZ%$j{)Wuce&DqxMuIo#)iO8KYp@}*>h|Z**|I_RuMRmj*TW#7i zeqc)`!~3wYp`4V@!=d{z4MA6Ge&muO9-NQ>Va(rWjUlujN2}M&UldOTja>ow6zMz)yk>|DkAv}!&Z-2GG$dk?xetJzj4`@c( zUTg2uA6MzOMro_;uvuGKpBN#;aEE!rdlYfU=-2FEpJE?GSrFI=CAd9KjqqM4K{;B9 ziF;XDIf}TY*6O6sUA`;QIt<&fRx`{h3470MLcNIIJN=9@_{AfC$i=fn!344BSbM`h zS3l!+{oz4U^zohTRo&A;c$cE%=a+_~lU(-K@%#09-3Ez>rblC$A4b3lXJw6%;eO7< zg&9sS^mI7eB_JXP+isntTt}C^jpqs))y~x@eEbAM1!9BdY&PRz=EVfopALeyZzi*K z9eW<8JjxnJE`sI&bG)*d?BQA)^=}OBlRtE^x6qA+1qKUt<7iy1rh22C#l9P@rn_C| zoIflnysrC{N@x#h^P%RCDPx^Jh(~u{O@g%j#Ah{U*<0-8rXNWqPKHu#kvPBlfOoYK zTu8c+*vttsvxw&^U$YtWR4d*~2yxnLIJ(&euUqMWNSQ-tw_2=a&wvvudwiG!6vt=z z96(U{%G0vs!lv2Zinq;Yx%?gHyuf-Bk-DqGmPHxkk7R$>uH8osvjl$l=E z1f4JC``#8ScOcmbcrn@)evOe7sm0IPa!hm-yP~phJx8_y?$wW6h$TPX((68`p(7y8 z`?kB@uYOB0h@ZT&K5Vh{7HHg2^*uYf`Mly9QA>IsPa7ftT@U+KqTlwbUV~I>e9Ec> zxQ665t-MTP>cSHMdi26fJZwqg?DZWDl~7-HU(LbvxULGP@>-+t_1>b05WIjdn05v8 zmakdQd8#RKct4(cq12|e%ehCJtCjkiQmSO00Z)cNVam*yF*i3cB`Le0!l$LhI?u8v z+fs%;J8U2CBHc)TZ0*y-Cmo4;`OtCvVumptv(>H8;QEb}EAj#)1yGTmwP%=x1}a<}$CvxKUv`RR(|N%lX=Q zeHtNg@g$)glrS02^g22{rm)9{by8XFgn{Z3+-^<518V)dUX`KyZ1v}crDi1Fq$(Eb za{~LhI@})N2VI}`FBX}7I9D-7`0woWq1&|J)Jxe=hd#n;bR1u`lPO18 z&G`gGj9ocf2>+HE`viM=058+_ZT3FLk-^f#`H*#B!cTGym@Vb7jez2Y0~eq^#Y!<3 z`Nl8-lH2~CiGqbdYdClo-ow#kPJw<#254L&$qgZ9+wpT5jw$EN)s3N4oSY;1JM07X z&U+;rE3krXKn<16)(F|r_rUGD*!kBHnFGKaD$y`1O`W*Y0ouz{!L3xU&u!NEDkNLL zZ2Q<(&&Dr)3o`jU0y7Ce^M0AU0>`GJLVClNd|$MlDJ(%}H4ckgXXOe^tXZCK^!{;Id?fnRsL4;63gN8jCs6Eh>f}Fv9pxkMBoX1@s6s!wT-rhFIT{;t-$AT7cYd5WpZ}^DM$34pG^d*@ zYoLpcI^+WlPgRaF-CXYHcwM$?6Wgnyb3#(=qV-B^_khI~vmsOE7p>9}0$Jbq32WUA zN?KA-@m@*X>z8UZYQ(4wYe&R*28CLg*-s4B2iAVjQl3&s;mb|Dl|9T!1p%_hK18+5 z%J7zy(qhWcP@FL|6M?fA8k~aO>kWO`wDk!dK;^0NUO|H1pYR>X$L7`Ec3Bo&;2e$F zvYXSeeSD-OW-Y;+f1!fWyiVgUk?@do8HX~&SSl@dINmpYv_}h3X}XGZm3G`?^T{Yn zG;o_gh#B5}xW%~LJq^n#k8rBr$!BggHMkEDn2)3B2W~0=fXtCI+rzV4dlfo6BEG#6 z2I*=pfQ@v4W1_1bjbktZ92{2&*-MxR#oE6c49o9we*P*?{HAbprrUF4c}*Q8HXrgh z=8uD`#!MsHi%TBPi#|c#&zE~m523j>g&~+tbR{;r>x|o!5-`&|5375esoicgesQdLDh%N!QL!S;XN z1=yWqQ8f$m@mUcz&>PYL{G#p#_`t#a@)G(E`>_H=?8a8(5~d{S==m(q$pY)mXkOR* zuvr8eDv^P(vCCiIsxxmc|U)vmA72Wjn3y@uv zhgJS0bPKkvH~*)ej%(f^9I_hvcSrIannDwi>}u8Nz*q}Q)!}@vvu@7X1I=Kfn9sz$ zmaEMdOAI#Vc{J>ciU(C@ra!`Mjx?={dRY~U&8FN_nz(lFXCu!ZH?HoE9r){!i*(;m zvs0+u2zKZ`S603qU?P75oH#oz33U!q>Ch8I2|&zv2B+6@cnDz`&K+#OkP2M-MU&e zfyCtLdJ78~_0-QA(iBx6T$prsXMl|e5t0;o_rFu;(e}9%yX?Zp@ADzM`(}}!fsi$O z8t0GN<0m(|{F^&ob}R@wMP~FNotWZY8>%Hi{$M8()uPP^Us;>`G|q*owSI+j>x27v zn!f985RurlM#dqcg0r&^MK&x737rT(SsPq=KMz=?1L9j^b;d#UaeX(M(FcQqbfMIw zeyKDH;5E=hE1nF`3A`D0jl-=}9N!A8F%>)H$*Kc*?UV`lqH%kuCWEI{+eA3kxQTOc zS9kQ^X1;3!%BhuBm6I2%Mmn*W(o{GSnwh6Kk&99F(9*8bp;@n}l>!${`+y4<1AS{c zG6!DgszP<91!7ic?2d|P z?fO9^U(R#O*>N^)fAv6`)f`t6GD=zBSxi=58?DEcgAKJm_W$xCyK z5o8)Yb((*7(b*?4Rep#K!1WwwEA`YJ+@c#l%WN$gU8%I30b0(#T>`!8dP`&ez^lE! z#-lVdQ*Nbo%ryPNQ^6fGDPNa@Ur!;Jh7BxsU~@P4{qhqa-(<#vY)O6j+5@$9Tp&y8ei ze5OaxaelDG*ZF~c*_%KjcTu}bg(aUaUsLpap4k-xxo|D(OuFEySw1#+tzakZ)mRRb zzAfHliErFf0pPa`q94`O7f*kWX3w?%GU;fuoj=+n8MCic&6YK5dN6c$ad4_LT2?Xt zIOQ$(acAC9g}r_D{Ri(hf!#@sOwy{YT>TQ?>s}e4fNxE(Z zP7{&UZ7?Rgmvz=hK4yKDklO)k$@%7$Nj=PAmz1SiTAz_N_N@+kHtSt3v8bfH>B^BU z8g^Hw8^Md8AWC+(YsFS?17vP_qKP9&#MJLmj=ggViCUm~NEFbFyVUgzk9s$Ta%%FT z#Cq(JSdP#sutZ)5)sIUg(2T}){F$qVXFkiQU$=tmA#pi%*WPaXdF3tj$TCj;xJEaq z{B&>JWs0Hfr zUM&W_g?JT`@B9khuzR_}SP`sq*TKtzZH%t_!F(*{?27Q=TF8G79!@%H4AnP|d^*Lu zUSoIb;wUEI0g6pbd@$qd$&r0FA8l`YdgvG?Jz6Qok1#6fR@;7N}X8$p(#+@?BdZneZ{s(Q{jprce+>ul3%g=#((*IKw_P}K}Ae# z7@*ZfSvI9K61%B(j@#n@lq@sc99LpB5>quAJ;0{0ZFdY;q->Vo&kCC<_!+5HlxM_9 ztDO%m_V)9QN0*7@IsXrP?;X|T*6jmAL^QI^lHD0~p?NjkfDz@Z`^Po}U!lhp<|KW&O=+*RRc;N1S#)@scJqPV5tt z=Cy$FJ3g-Ffh%7Kcc34+mc*Huo&YK?k&hewR-S!bO0|8P`1V(;A&qCD459lOvoWms zw{r70@#AIbdr3N3qrP8U`F4@b6ey--N^#R?zki8*Pp!9T)C5qcV162A96d+XICY;FAfb?WR+0=OT%lc3ah{PPsUP`i;!FXS0LiocmKwAKHHQ*nW> z4wGrvqX->7I7s7E^v#7=OT=NjQ7)6MkCp}-K%bX0S`w*jE*r^z77>vTfAI13-H;l?QpxRJSo3$ zw6j*!LZE#7eNyIe=2z|>wrd3tz+D(2cm%fCNIN%HX*T_y9Fv;pv*<5(Nq*yKr*QCU z?WtTw7gXc$V*crGUKN zm*Aj+v*%Sj)|cVR5f#-$D2&ZzHoo3$IX8A^*ryM?$clKa-d6v)ZQp4WmU>V8 z7~Pw*lqIq>2KYhN-k3aPr%<{wOc9&(h!+M}+E`7&Z%WfdAhm3+MXTLMeM(v6IK)09 zsM4%iRd64Pahk?ggL;a3w~rdUe>(`%6T%!>^hWQkAF6&qxW9*%MG=t^F*KEwGRQv0 zNsQJ8*AHIMb_lG$S!TeA@?i@^(N3@VQ9BC6DGByyJSb0{ezTeDGjSy0tdF6RU&}%R z{K{VO@L(A_c#H+ULYr6dZ(q5D0L2~D@&3x}D>4FnSTTWol!F?wkGq8(g;QABgNW8y2&KDH=ys3h1=4}J`T=p=P~`CYO0n*17?lh+)3Lm!N1ua(`ac3xFV(GcNkW5e zs_WMk2kqa6Bsfy~<{p%nIEe{8jx#T{$YE?$bFq6GR;n@rGc%%mWGix9I$C~tI>|t% z^IF+@G-Z(G05~WojVL{>OE1iFq-huiztb!rJ+<%y^&TCCIMB&jJy*d?lw_AW_YI5= zbT5}TIN)XML~?qMUqi|D?)_^Pt(Rd&<>c;8!cC&6_7{qDzvnr%2^;fYhCqOKlJ z)%~GFo#J8`mKw)j^BMN)LpbyPPdMYFktX~W=p%e_)5;9nx4_GsprcaK6w!jRY6gQi z<&7NgRQA;P=-kmzOpA_KmFwtgylxf8gVhUXuv&U<81{rHW3SDBC?izB(*_0K7FX_B zI25JpoP8>NPkh)b?DnU0gYC*qC^4|zIkUuNpLmXEbfc6vU=Bg6^oV9%oz4MGH{{f> z?9J6@;(D%2-nd%2u>mA&PBZOS2I-5l&97d(oGS{@o%afiwKx7;^^)9^&Q7SJJflzG zZiTA{-ZT=Mh=qHp*c71PJ zj;v&eGj$aVvKtHMJX|=(BW|=ZUuNsr264S%ti9&$a?LLozB9~jjVsgpjCpEGJNrHd z`p3W|FlvN2&(J!0S+vn$^Yl^%>65j=JJr0h^n_Q99dCQpYi{#b+Zry5J8AZV7cZ@m zFr8jyBtZYMakyCM`xC?~`2{Zz9S4K)sMQJ9!EfOj-~mS* zdpV(O{4S4G2ujbGP5LZi-gRp2RBuBj{4&C4e->)kRL#UBbmbPqU%jb4w;NXe>Ao1i z@#TVk8Y%_2KIBn`ycH79p0oDxf)kL;V)79gq34hh)|aDyL8a2USga~o58>9NB}sYZ z(s@BTYo^s)L;tLL8Qy_HthNM-mR?W#Sc@)NWD~M{q2jy*lSV-oL(WOKs&eH+Xl5-x z`!p3fFT6hmWSh=*KLyxoXO1k|N^4>t-Q*ay)BqC*MVp*5D@_+1n}dq6O|v0{ieK%3Zc%4kwhkEct1V`L0NpvB z-(!R&yYJil#w2(AIaxB}Y^q@QQL!weN9IV+M&*M2Jz1XznSW{h0D={HJEniV*lTl9 zQuj@L74cru59d&qf1kYtAEU4>CO0Xg9UnJ-LG+|CNXq}Zdc~q4|Oxx zvi$cz%x1*)5xaQPtogS(*2kQE(CvX`=g6wSnKiem%0 z3loCqoc0Bv!UH3pf!}D?{o+i#syKgOH*0YrcBM#2hdlZL)qil>2L^`((B129M>)b* z?tKn(RuK%>Onh;vT+s;a7!<}zw+*if&B%HxK28wJ>g=i;!(xTd`c>lH9S&Mc_iKhQ zAg|L`-|Bdh=eXF1{JZoT{G4JNITTHxN0!en#-#jv%x3=azr}3QIsLGi7e;SO!jvxs zUEd;{_xH5kMu5`CG!&e6>>#s?@B5LL4TEyQFldkxRZL5JSzPI0CT!vck@{tG{xJZ3bZQ-vHeN+x?Hw&8By%rUJF0%rL8@OyInTvtMl#v44AFL@z__ovrFaPmzxQ z0NixXLe!n!WFD<*B6g!RvF4hc_!-;Ct9T=s?dg8bDsMvirb7rDYRKFU2X&^y5|WVITPO3?;D;dZadZ?1-=*TP~*e0$!0V zWC&{KAxE~&ePK5|kq_bHH4;S}sz#TjXzToJl{=62wN1VrTU<%oCZqdpcnE%Gi|P&z z*urZvj#zVPNI~=RRH}i@9JCgt!%;9dLN_KqCLQ7)f2JR%UUoOw`D7oZj|mAZ;Xs^9 zH%KzmkVfD&W9_3;3w~%2hB;-HxhGA-eP4Ie3#VYR+PzU}s2>_HbBrC3jk6l5hcN&? z@!nWwN5_nOmA+hh)Znrgp$4wo@HFn@1QDX^&D^pwJvH=C)94@sG8bWD*m z*Wr(lgM#TcU%coTWwxL^W7Aeq&%XpQQ9i8lET{1pwC80YXcc&CkgCeZubbBDH)uD_ zu?;WYGBb+L)i;%5!Pjj`(2kUr!=2shZkx#z5fB{f%Jn< zuZW{LcY?JBLneMl58^-a_4M6O(Xo-g&0}j@tXJ3ytz}cQk#1T-PmpyOKKaNnah}2m zaXM_uim0ffV0V8ix!3e~RW+}L6uDY^PwzW=)LCY@DQc@L^Xbk{_|f-@^UZOv#h?S@ zSu0Y!AkW0R35{u1hLaPpMfKCZbrkd_D#{nuq?XpVzuH=@=uf$!S+F>o%lq6(upWNp zDgWq}em{5htF2gksZXNVU>n)@AAxbnY(DyvPRtp z!*V~?FDFE|z@8L_g{2-;gcw>E=cV^{pqIpt5sG8rWQO`*1bu`6{{(%4^2+}>POC6Y z#IWzj*)G>x>7Hd95x~JZregvw{z-QoDo)T3UF;pCzA)g0*JKt(Dv*v4t$jX>Nn^ulk^1VhZq(ea!F&F8}(g(U8WsdLMjVusi>K6VtQvBRgU8@sFQB zf36AmiRq?HY)s6*sT%UFsu7cVLRxV^HS*l`Ft}i*liA~9-bqgM3tW+wC+!$PXL|Pa z`AIVB!~8yOM7-%RXHr~*7o9n?->cKRV;6SGj92_gqX_O<=6<~_`u z_hA-`8e@dfMo()#dAa4NPMIoYp;sN_2legfhSO0lB?vy|0iNas(H@U}yFeFIEAa9o ztw+h9jt+I$F3ZPkdN*+29kU{r;m+bbI&4th#@YdYNdWM(+K3C|#_mB%x`s;Ils1`V zbZ46`G$UY%J+nGcRGaa-F)=;;VdNVCJ_;!!lB3^ggn;3B4>mnqvx9 zaeLmnXtdLNac4D?iKC(jPAcadArk`<)^F&W^l z2Y{*QgS1M|&*v}}%_~~92HP`)FAfySI5bC-6%lZEJ%z`atbw*zI;rK_mQ_nfq@qnAb>x690tfp+Y z;q$)+5mHx>zVtAKjQONCso&OT3(oUvJYWCt ziGX$rKMeQfju5V?IA?;k+|P))hZ|M=mJR{#Nnp=oFr8^b*kJ8vhSj-!zsPLZ7rvK; z^*P$WK(5=NkZ0Zd`{iuak%sSCwIIXgx44vv_M&jh*p5nHk$Ya(M8(Y7m8SEf{O(+H zwfWP)@rGbP$UyA5*`LDAK_u^Jx1To>^16SYbW6mOIu%#U5-Cy14F}>4Hy*xu90I}} zsT+l`Y1?BXP$)*$GPRs zQdVJhaSB@cG*nQ|N=rj3|GYm>7Ovl@I~4!{RR?3(pi{_K6|Ur^rMJ|>Z9VcDaQ1L- z>$CK|Tl0FKc_Qn(#02UeR+aeWZOk*!HbIVatAM+CZt4Xy{BrEENx|V!PkADfenkJ& zAy&&i(|01R*a`TQ6ngGB70ooG|I@L(7wUi2GleWeZFW#+9`!=Ji^*#0HfFqS9o)_sZge$p^#ShdKtw{U3O z7jpinUMs~GlCtpUG+IHNqEmsg3^$f7`}OIyfH=oSo)Vt+*xaCjIgW-QCr=1#2cvi2 z&Iq_NX}xy-HLu59gDv5KtBt>fiD-)7*!%{|a7%*trR9f?0QXrrGxffl$!+1K#pp$6 zzf?iYFyCD{Ox9Dz`XFPPBeM@o$#{Tkb4>Oebe`ghHZ58W; znEvp?Wl_M9IP{#p@gh&G#H|eZ9o&`SGB1tOiB%aaCYT^GyH|D4A$c!zX7-oveLVP< zI;@jT24tVX$vabIt*l+q-ektx$fqIO{fBKRm^o-wa{zqWf9hN_ZVJp$)(;LRC~)?F z8s2-j9*6g~BmXkIUo_lyGBeL`^Ao%F0Oz|}`(#pZHaEJ4Sr-Jj;U98$*czn-4Ky8( za%kgk$9qIvZnWxMt8l!FGQqM8OIh8$Z1Zx&Tb5Zn1hsQ;+v;GR9!u*>@MSitl{InE zL4|n)t{@mB0ym)h>sQDA%FSQ?vk2Xo1T~W2(<>-M>Ntp@14T79mz^|l0|{d7tsanN z=fzy~0BR6Xq~iQ0D7jv{SlKG_**P&g1)l}I#H7-M1k3=rt{Bw(6VF8bZBeA4C8*I+ za^Id0?CH{;;j5@0YG2-F67vWCjLUGBVgJWzSpV+3?T>fH@aFs0!mlg;CR2!F{~ehE zcHVh4CsmFrgW2?&dSn_d0_;cQq?^+IZ7|N}Wj%SDU(Z4BYGMG21Kt~JLhzT*NI@0R5tyfRgywzQg0{NsdTY~CsIV=NV#WvBI z=h_mxDzCYYUrmi>5^W-{hDAjruGeW9s&iv-o0hRDCGYgD*!>Az0f2yfx>|ezof0_| zlQCGi`y^3JemO@ZbN%;lj>U)RzC7bcg$D8K1tMBN!7QFP9f|hBJOeG5P#?Od&F(!( zXdP5KBJd84DAf;sd&TgG3)Q!G8CSL@tzmL=u%p64Pl1HjXfM=v;G4uu@ACHIs4WDl zg5-H#WGX*tdHn}$e0_fCe+-77#+$^2o<(WwUe>pWn{j-i2W2lRn|L}^8vV`p4L|XH zr{31yorOz+;N{GHQr9mk!N}2#b+s@pAL2L&-z6yYO0s_oq$;iVq|=N*-=_{Qm9nm> zqP(&yD#lFXuVO9HS1SV=}HT z3UhYdOI$PF)HY7|GFB>9Fknc6Wl6zObvqDIe~8D5Ki&nBZR51zz7u9F8@RT%sj_J5 z&hTRB>VDA`G#`C478T8h$;px!x~d!dN3~R+2o_wl_E(~mR%+uHjmS*SqF)RnMuu`4 zD>zMdODWzX8gd!8Pt|`~JhbxSJ-nNTj53}3Qyapwa`@O{IQvL7RdMc&WkwY%{Yl7~ zPBUWe1>(k*j)MxI@!wG%9F&ilbU`RD(o=m7Tku(A^su4Fdg-hDvU7OwzE8uq#m}bH zK;pr_ideZI|A<&I9}iCBpTK}mweH@2{2Z2#L}oVeUA<~C180Es0{r~9VlVyr>r4XG zpH`or9o%$BORFBo=efgeH6*YRVGl-|pZl2T9=1>;6we4R6#N(NkbhA@lCdqz1puFu%Rgl@xM3O?_R~?@D!vI^&V-nU3 z&TlhLYYI?kyRKFLZQSr(R{> zK8+Om@g(}VBz@vJFwF@6r*s7^gOTNS`0k%11$gq=Gp_@`2ZAq8w3jK&Xt(MOkx;US zZ^pej(QCD4lOYD1=kF3GV15kI&k)KVdKyQ6``23QTN3r#Rm}+r@O%A#d9l!CqEDMT zxYZdO0EM?dZ;6P zd`r1^Svc+N8E>0%v3F}sk+i2(m+5tPntG1z@vizLO)IeCyKT5-88k`;m{V{?e#Bli(t2K{8mCo_6(8 zKz#inqjc1S!or)Qjgx?{C%g_Tze4``@Ahwc7!NHQZQAW!<9}R+!Zm2?BX%DhO%u>Q zCN;at1awI&>%%%+?{YGLa7jS>@^CRNTkl0>U`6q~jUU?J&=9_yLBaubW2<14_H$G! zltS#`y0VPc5d25LKO^y?QunHu*Woi?`8A9c=YHqvs7axpgz=UnkJPz=64XFdStjR` zP?aGzv*L(Zl}bcPV@87oe<8QQ{NZr+VjMi^ricRScOh3}A%hs8DQfUJ#%2*HQySkpkBLB1g~C!?aHI(1Ra99&n{z}+~Vvy z0}=23u;#e6Hgxobb!+g8HMxYmKGM`eHeJSek^BA5CO{f%c>^CN3xj&_f40LCo;Yv$ zYU1IPW%8n1k^@Z*u}8j8VA+ZGhz{LpTPkFyOsGQAr(X!Y&Hlgr!peEpi;dCr+BkuAut*6tvQE~yngR#l8=WMEs&&~^=y`qFQ<~x$&{J$ng zIcAPCn8AZ(zBgNft|tn;nIQ`J$47=KlQ6 zOnX?2v3oz$UhB2!ME^ZnR)86zz6yjTu+RG~yvqIKXyA?9z7%-t#%f1Lmx&{C5+jcP z`-ONG8_n)4#bMH7+nxE2vRYUGBgS6qjIp=9^f4LPrOPJ@){aDPwCq;5?hFT?u^qin zep^P)_gw&L#nH;3f^d?J+HK@9= z5goYjr^mFTlahVok9%H8qBgN993jl$3n}5hcYzcg*^ac!f-B8M(tF=OA|${+t+y~? z*_-SdrzT~f6^Z8eySav!=_2H=z6)Vva(Ab-pwWeX-gEX@cT2OHJa#`h%a*nB{qpxC zY9tKSmM)vNYq!gaflU>%yvCUmN^L>x@g*0v7xr5-3;>dOCZNJrOK*)yh?v7%X;OT$ z#Naeat~F}8tsh%GdMQl|T%0{nrE8~DKgn;2si?8WI2Kt?D%N2T2++HG6@{Yw@aV)I zDEeo)d&6k!Lc{u$oQuB{SKytq>kKYbxKDEVy!5#Ji-`6=_+TGSm)!FTw+Y2#z(WAa z%4e{Fa}Ua>AOV&eaK<;r<9-#sRYgk8ZQGZJG^x=Wtx`HB-kiWYYCbu>-|ptPfb!d@ zRtQ@~WZ80i+@7ZSS~wTLmXHT$mY<7R?NHC>sHCp!u?pR}bFIHRjtU^yQ~^!rV&?9> z!S^6q)^tWtpYHP+UAWPouQy1%5S@^u`@4okJdhBFyE%1Q5 zVZYmi$D^ZIcDJj4Px^4@lLp%(J+1nvbt-5uF|>GA4&*tE)jG@%Zd~bm_Z%Vb7#MSi zy~xf{0U<>}vw;KBfS{IA|J1{*b8!0919)CpbV-Oka3>>^6MHXMSuev{&c+?|)LH`$ zK$Rmp+Z;3di<@?xzr;)9ZqVT@1Nm|Xjr}X`48i14h@XFn?i2wSgevw9a6#$$&2x|b z9>Iq<=!>T~uT1(iTBf_17pCdQ8uqlBC{r?kL1M)q#(5`RJVl!rKb=@sH;Z-r4DCl) z4UX@kw0D|~nOWiqfazxY+nAW&wa@+h^M(%XJ>ABnuL+u)mp4gZ`=;lN<9$f& z=+17mwHj2`Z{0fSfQ?hWZGUhwHZv(+RCFabdQ6#u6&lNMV=YYc?_c9yJg^oxfyPhn zfAads(O^x~sA!`YD%;pfjO3jHP)#&4-z$uXY$y;y$(_`h9Q z*B}kolE3pdPyh521IGWm9+Tkrl*Gv&Pu~1r={`A@?zKOwq7k+yd5zCqeA58%-zkop zzkCe)hdF2Z@EQi!B;;b6t{hABri^L*2_KRn%V)p6{CBC4Z{F^Ske{9ZzZd^X233sI z)3ew%Ef%*r%d)A#J3O$&M5trxZ8mzb<+c(rNx;$|8e8mkU;y7=N7mJvKhw$3?#~L? zUBaFouUvmcJIssllq8emZR^P1e`W?cr{Kq>0S}jbDUX`B_2;UCU)MD>S#2(ittINX z?$FKxhWPypd4v566bYER`OQM|8jpkZ`?wFK-3->Oow(`T#)ct-$97VltTA@3%sdEr z;Z~#zIKRM<$5tG6NV(-O^o8vHoAS& z0oSl^jxZj9MjKk>VL1GOF7Q1lA#U=H(nxXfW%YWT*!HS#Q=P(&Qh4#sI{`wR_%k^l z;;8ZK!eoPSZWZqisYS`}-FUWgOvdSz?I>~@Idje=)jSYjC7_hw;Ul2$L(#t>mQ z>zQa3vYM>TP(RLS-8whpMB|zda32DGQ#Ui+w8o}G97d4$uSZ2xE$MxXHFbJsH^we zp4eZLRn+&@_RL=zaSt}Ka7Y_4%+CV2<_g^$7Khl68oC^Ow0F=o!E3wzqWd=QmL&*n z2D$98K)Vzkb;lJLFC2i`a`Cd`u>8Yx?s`X&18u8nPg~?i{54Bp_cc3v5!(wKdU!8ZG;}cbx|RXKLHXcw;5!&8 z$D*JUCCq~AFpLdEp?qA!xkbwfyt)y852cNd5iV}LlW^T5xl^Irw#3Qo4E3uc=9Z!& zIEPkVMEMe%t6n9v;Z@qeZM(|ese5aT119XOl5e}5c~8sK-B!>$MSQmRoAj1h)7P4vdp59JJR0c$b|5yPVw-@Rgp?bd7CIs&`?!l z79GVWm?oe-t`TQr&DWh1p>)gbj)wLRM3Jul@-CtP^u z&FgIbxVe8iHH?=YbszMki-Z9Y7?&9>7~4!jX0inD7-t3n5e}!LM#xZI<1KHf7n&(kr^*u#-h#o7pojB`0F`w;B|^7`0` zgM&QD{Q2LFAOoLP$Z8aAPtSGbX|J@o_Bg+$#q-BQVq!Y|>bqK9`~4X0VU(Lc@PGFX z&;9&8R5rT#-79+b||L3AUBcBgCfX4|YP^r(k&oFJsgNym% z1&epdEF1TZB{iDD{OH~a(IQQdqv2rclrl5ZjB}~~SH^i_V66kD=wop{%V>WVQkf`p zK2ESZZnteo)RgaVdWMO~Fhqxz9WiE?6_NSg&jklDmrPgc(>Q*g?~9ly#fXqB8va0E z>FzCR4{+*=FXoVQAl_s>%k(rAHSA*9@F)mhUCq2>#Hh|R`i9X7-B?igLLn9-lvk+N z9rHlIq}1#T{2tSX!8^DzX&cyh1(hDzbb?gg3_is>5Sx#6)mL%Ca4bDIZi11udU?h> z^1Hf^TK@9Cw{rhe4}4Amo(Vd0D~s77^}1)i5BtXVVRNYvu#WAYyi06l`kIel;QJzS zGyJYQa}9Fvj-hy+g6F*F7;|b`YBq4aLOJfPq7#`$aUA~Se+nRYEEC^9PH_%!YDIM< z0~m*@mNXtsRZ7?mq1UlTv|+Xr2w_z)8QIWV87()?-hd+8cT0`k9_R9~>Xdu%AsmnWlGR&qDsso4Nmm z#&YegSk^RpqaqQaon(}7>5}&$+JM+$4ad@oxMGH<8Lm&!YRY9?Ondj`>#t} zEn-%J5vGYCzo9+9GX-XxS`LS!NM_j8cu$Oo4a}VMc&8) zoE*Y%f7{KFWo-7eds~W=8ZdNSS@@+=Nym$EdJLcSVS{yT_V$ASuyB(sbgw7H-?0Dy z4%*oXB-W8nU0={LN{O*E04g7_*=9D;_o{2LUQYYe6VrYlyeLPTsIemdQLZha0nx{nL&qQ{to3=P? zP!mfab+QffA@b22S)y@qrzx18kc@l~Gp~%W-3r*z)yj#Q`IijKkbiZw$z1^Hz^2JSq2)1C%Q6bHLmzCpuCy?}64EalGtId}hFrpSW}@xpV- z;70yJ#csV7Qjo~E1^8Wera8YPVxkvJL_gN#Zp0`MR~UFt z++m~bt#b*c`|H`nf(B`A!eGBu5<*zXVQxmjvR}x2wL&M)YXZodWbE8c&I&f|IhgF& z0ktfC&Y~IgAa=3a1B+4Z!$4^HXUc61xi0;P5a&gI#`0}-_kCXoz8ApU5f|0dnF*Nx zcm-J%6hz-iv*Ha14A{Fccs&Ths(!yLf^RHFXS%K==)Rj{HzX!eQg~~~VJ`FRd>>8N zX{hgGaXfr*xbY84ccX%z#B$e7CzMWs)biRrzowBKLamA}Z;>I+uM@En8ri*9E+FUq z1fesWSwec$j+%#6tbZ(q6v#)Ur2@2k9mk0I&O3iH7o!Ubu?pgerS}5-j?j`$PX=pN z6&>$z5?4&bK!eo}Y#?BX&M+Jo^us`Q|Cm?SCJj&I%ql)`FHIY+1YQ?ms|0$l*M%%Z z@Bqb{Lx&1ur@S#R23;OK(7(OcLfPv%SQ?SJ-_b1Rc}KZ`F_n;^icNexqSI?QX)5EO z5h!SbCMaoE>IffozTVH0%S!23SlpHDVVl~QuD!o&u2fkP(T<(+cAMHCXqNNnBX&{e zN4y*cm6aDYHMU238zlIsYlNYWSx*8uI2p?p_&acKRml0q8b;Y;`t7o29?D7IYkpb%5Fk&Y zwexLHMV{);xYD3Vs+ptjfRJ+$=Nq%CN%c39GRf4y*`6suALXw!V0)6S`XANl!H_9R zr$TXo)Zi2~BXHX%&TH{AUpGmlN!?wI&cQK-yHQOieo8-@8B)W6{T$I-UQ z%K_0kG^#_&b`o?kV4CXTW;;ycvhSNyJAzIpjHEw#vU_zhS;rNDpHPwu-fME78wXFM zWh|XZIWahQ?gb zM7vNkuX*TYM;fxngtyi`N+qy2e!WRRX?iMieJdt{u>DMJd#QPF|6s+7#5fpa{Y!YK z5W;5p(X^*)?LbUKrP{oOA;zujZ#52)7`M>Ck;>x-P9JmI+rMk^+Yp&>j$>i&!LDZ;VMzyckk5pt& z`}1|~@iKmCDrfYjD<%+zFw~&%Jl`~VcY+d`wP5Hcp}p9#Uu)-Z!~F3+CRD5YIJwDBAKy@nSz>V=0OE_|I zR3oZ0O+7&(Xj01yq${Z{+8h;R*m&5UWFP3`voIOtg^$Wi)QD1-TwGUG;hNgmw8kM5 z350?aU~mwptY97`H5oZTEwQMShaJS`D}`ItSIgW>lAUKKbu4c?&XjYd)=>*Y)~|wy3?ywB^T`8Q35Y#Oh=l zb6s~s(#zocWj;9j<*$i?^YTi3$%KpNoC_idUcK^Ui7v8IcX!2~`an`J;I-))Gr<&(-gLJhsK|fB%sXVDc1zZ>^kbKwegZN~l@hJ7^@7Hv`;3^8#Bxu3@MchyQWq7 zc$eq~Ae0elPBox0U z8Bnheil=dS?R?gWNq!-DW^rJ&T4;^W#I&7n+_Pm!?+LzJqlS(+p~TT~!9tLqY^s6y4BO zrP@|&AM{iYXNE3I8S28U2zXy)gR52qOiQ$&)FcUF=trd#xfuGvpCf*7rJ$I$r}+o zGGhAk_8xbg4vS25hR=HT=0Z5RxZ-vk`x`nmM z-}bxKBzR&o$803oc(xT;>+wWv z-czR_@|1Xj@8f>L)JVC&PSwQQPU$*;D){| zh%7HR(}$9C%3ixbKR&J$WJ`OATV5#~BCQlWAjNqt)Y~Rq;jRk^8A+fbL;=bxox@bx zdGe%(u`MN+lyA1u6EY>3kZ&d?%X)jU(B2vm zkSJxF*punvqOGIR>Qwg2>u`jt*7&AMRrRflo_)r>_e^_ZyXrh_Auze@<4H`6+6vEx&ux{E9&x!r`vU;;sfAYEipp$yCsYpo|d75ewNO9>T| z9TDSU5;X^nm3U6M5)UqheJ(V#t<%}n&mnQ!S&sX3@&4tN(mG%!ArWW6X54P4s(1e4K5DQ%fO;Gs9N^d5Q5l(S56D3Le}Oom_(r z;A{CO0f6P@Wo>s%<>%Ex*KJBqz2wNIAS&Rl0jZ)SClt9*g>U-*5i)%`JvgdZ?s%nlD4yUx31guq5 znpBjNbDB)TGmEL)V(L65YX|z6xghVfa6;l>??Q9lq*JUZm7BpAZl<>JZ%`34TeG!>A zM9DZohBxKuAJF8`=wdldNGf>WLU+xq!*KbxKtbwz!T!z~=aS`=$-0fiNqBV1`+-3lUjK zDri{Tglajr73rm37hD-B(d(?sG}BFBc6Kq)z=*G%_On}EU)K(aZd+PdFImwyS?Lb( z$rv-zk7XHqmy@`FEO)kT5?iT*6*F@r8s~Av5iZti&P)SIvrSPVphf-t`LfG%e6|Xb zW%V%##Ekj*&asQE#*GA%gFQbH*W+Dy_P4>kJ=Jb2x&tXq zwz~%s%IovW8?ILt2NtK3$aYA_vi^j}$rSV<5#PFxGqQOL3-Wcf4d^@XNm!KXSXb)m zCgcRnx7qFX7DcUWk=z?y27@w0*ZfTkfaRjn{_Fkwoh9tcyz;^aJcy@6)LIH)MIGCJ z^49vw-X>hu;ZRt!^xuuAOY7RPsyS8*YP+tbAAX_8VUbWaqK%@e(vU##`UykISHQSyDq84JDyxeq*h`qVz}HI#XAr5x0fMLP3dZLG2E4u zTgonk?D)YnmNjuXN{f|jlUKF|#AL;-{ZzOP2e4GxP9#F77$3*7V!l9kKo1_3zQU2k zD>mp5U#{`^&A1kZ@z(~oZb?A!vFoli5mQw{mS)vfElSay`Nj#6D{ekMj^wWemfP>H zgF8cpb+HdOUPbT%MY{`3VoOVsn||j~9Eydw<0UN9>wdA5bEE*|)Dg?;k2^aQ)|`|3 zHtyJFGzCw!B6tj|dM4=Il!a5`)A7N|^U2&by>yQ024TwN2KMl<{wM)DS+7hQavEAc zuqO`Dr}@avnQ98%+9&}h$F>E5sV&@lQhI^%tWc=K*X+bDYByWOj54&-iM4HZL{9w3 zX9_I43UHdM(XfpNuh*+#OZRLW>ER;_8b|NPn<-_u`P8FB?_C!))k0oHQPZ>z(P8{T zW{YQqD9C4Ohy4EN{cburB+hGZh$aiCg-!SmJM8bi2DW8PvH|C&!MdFa(6+Gx2S(&* z(6w{Xz(9k9nm9<}n|tAokOr3(soyAF=?M;2pAK>~0Ip|qALTMoU7rF(d&%K${dCuB zaP{Ny$!{Lt=!39zL309;MvwMv(khtFlhURkt6wTMOdqX{#B$3zbi_*iRi7ji-N&7NDljd@ztM!-(h0gwg{H?&uwawfK)z8%RK)4(mU*E z9YxJNddDvTb7bqeT#$5G{U}1NXX)t6n)XyezN&VwxqE<8!*p7toP;6}MdQU`c+*^E z7C%PF-#=Sq-y!O>DA~)4E3b>55Fcc7yN6jLSO*{6obLSXM5}qZ_*q8zQ<1!Jv-Ahm zW-hoLa;4L9*Yw=i^U`g@7`OY3?NSVcy7bn)Q@+m{yABD<%5>N%0Aq_Tqm25_KmU6x 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 LFSTc`-uZt37=kot diff --git a/example_show_decisions.png b/example_show_decisions.png deleted file mode 100644 index d0c256c837984926125fb55adc66bb04829ee6ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78585 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%0D_u726yJ|MA>QuW7@U9p%< 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.7.1", "autoflake", - "textual==0.65.1", ] classifiers = [ "Development Status :: 4 - Beta", @@ -51,9 +50,6 @@ test = [ "coverage[toml]" ] doc = [ "sphinx", "furo", "nbsphinx", "sphinx_copybutton", "myst-parser" ] dev = [ "clingexplaid[test,typecheck,lint_pylint]" ] -[project.scripts] -clingexplaid = "clingexplaid.__main__:main" - [tool.setuptools.packages.find] where = ["src"] @@ -97,8 +93,6 @@ good-names = ["_"] [tool.coverage.run] source = ["clingexplaid", "tests"] omit = [ - "*/clingexplaid/__main__.py", - "*/clingexplaid/cli/*", "*/clingexplaid/propagators/*", "*/clingexplaid/transformers/__init__.py", "*/clingexplaid/muc/__init__.py", diff --git a/src/clingexplaid/__main__.py b/src/clingexplaid/__main__.py deleted file mode 100644 index 7bd17d7..0000000 --- a/src/clingexplaid/__main__.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -The main entry point for the application. -""" - -import sys - -from clingo.application import clingo_main - -from .cli.clingo_app import ClingoExplaidApp -from .cli.textual_gui import textual_main - -RUN_TEXTUAL_GUI = False - - -def main() -> None: - """ - Run the main function. - """ - - if RUN_TEXTUAL_GUI: - textual_main() - else: - clingo_main(ClingoExplaidApp(sys.argv[0]), sys.argv[1:] + ["-V0"]) - - -if __name__ == "__main__": - main() diff --git a/src/clingexplaid/cli/__init__.py b/src/clingexplaid/cli/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/clingexplaid/cli/clingo_app.py b/src/clingexplaid/cli/clingo_app.py deleted file mode 100644 index 83fdb00..0000000 --- a/src/clingexplaid/cli/clingo_app.py +++ /dev/null @@ -1,340 +0,0 @@ -""" -App Module: clingexplaid CLI clingo app -""" - -import re -import sys -from importlib.metadata import version -from pathlib import Path -from typing import Callable, Dict, List, Optional, Sequence, Set, Tuple -from warnings import warn - -import clingo -from clingo.application import Application, Flag - -from ..mus import CoreComputer -from ..transformers import AssumptionTransformer, OptimizationRemover -from ..unsat_constraints import UnsatConstraintComputer -from ..utils import get_constant_string, get_constants_from_arguments -from ..utils.logging import BACKGROUND_COLORS, COLORS -from .textual_gui import ClingexplaidTextualApp - -HYPERLINK_MASK = "\033]8;{};{}\033\\{}\033]8;;\033\\" - - -class ClingoExplaidApp(Application): - """ - Application class for executing clingo-explaid functionality on the command line - """ - - # pylint: disable = too-many-instance-attributes - - CLINGEXPLAID_METHODS = { - "mus": "Description for MUS method", - "unsat-constraints": "Description for unsat-constraints method", - "interactive": "Interactive terminal user interface to interact with all modes", - } - - def __init__(self, name: str) -> None: - # pylint: disable = unused-argument - self.methods: Set[str] = set() - self.method_functions: Dict[str, Callable] = { # type: ignore - m: getattr(self, f'_method_{m.replace("-", "_")}') for m in self.CLINGEXPLAID_METHODS - } - self.method_flags: Dict[str, Flag] = {m: Flag() for m in self.CLINGEXPLAID_METHODS} - self.argument_constants: Dict[str, str] = {} - - # SHOW DECISIONS - self._show_decisions_decision_signatures: Dict[str, int] = {} - self._show_decisions_model_id: int = 1 - - # MUS - self._mus_assumption_signatures: Dict[str, int] = {} - self._mus_id: int = 1 - - def _initialize(self) -> None: - # add enabled methods to self.methods - for method, flag in self.method_flags.items(): - if flag.flag: - self.methods.add(method) - - if len(self.methods) == 0: - raise ValueError( - f"Clingexplaid was called without any method, please select at least one of the following methods: " - f"[{', '.join(['--' + str(m) for m in self.CLINGEXPLAID_METHODS])}]" - ) - - @staticmethod - def _parse_signature(signature_string: str) -> Tuple[str, int]: - match_result = re.match(r"^([a-zA-Z]+)/([0-9]+)$", signature_string) - if match_result is None: - raise ValueError("Wrong signature Format") - return match_result.group(1), int(match_result.group(2)) - - def _parse_assumption_signature(self, assumption_signature: str) -> bool: - if not self.method_flags["mus"]: - print("PARSE ERROR: The assumption signature option is only available if the flag --mus is enabled") - return False - assumption_signature_string = assumption_signature.replace("=", "").strip() - try: - signature, arity = self._parse_signature(assumption_signature_string) - except ValueError: - print( - "PARSE ERROR: Wrong signature format. The assumption signatures have to follow the format " - "/" - ) - return False - self._mus_assumption_signatures[signature] = arity - return True - - def _parse_decision_signature(self, decision_signature: str) -> bool: - if not self.method_flags["show-decisions"]: - print( - "PARSE ERROR: The decision signature option is only available if the flag --show-decisions is enabled" - ) - return False - decision_signature_string = decision_signature.replace("=", "").strip() - try: - signature, arity = self._parse_signature(decision_signature_string) - except ValueError: - print( - "PARSE ERROR: Wrong signature format. The decision signatures have to follow the format " - "/" - ) - return False - self._show_decisions_decision_signatures[signature] = arity - return True - - def register_options(self, options: clingo.ApplicationOptions) -> None: - group = "Clingo-Explaid Methods" - - for method, description in self.CLINGEXPLAID_METHODS.items(): - options.add_flag( - group=group, - option=f"{method},{method[0]}", # only works when none of the method's initial letter don't overlap - description=description, - target=self.method_flags[method], - ) - - group = "MUS Options" - - options.add( - group, - "assumption-signature,a", - "Facts matching with this signature will be converted to assumptions for finding a MUS " - "(default: all facts)", - self._parse_assumption_signature, - multi=True, - ) - - group = "Show Decisions Options" - - options.add( - group, - "decision-signature", - "When --show-decisions is enabled, show only decisions matching with this signature " - "(default: show all decisions)", - self._parse_decision_signature, - multi=True, - ) - - # group = "General Options" - - def _apply_assumption_transformer( - self, signatures: Dict[str, int], files: List[str] - ) -> Tuple[str, AssumptionTransformer]: - signature_set = set(self._mus_assumption_signatures.items()) if signatures else None - at = AssumptionTransformer(signatures=signature_set) - if not files: - program_transformed = at.parse_files("-") - else: - program_transformed = at.parse_files(files) - return program_transformed, at - - def _print_mus(self, mus_string: str) -> None: - print(f"{BACKGROUND_COLORS['BLUE']} MUS {BACKGROUND_COLORS['LIGHT_BLUE']} {self._mus_id} {COLORS['NORMAL']}") - print(f"{COLORS['BLUE']}{mus_string}{COLORS['NORMAL']}") - self._mus_id += 1 - - def _method_mus( - self, - control: clingo.Control, - files: List[str], - compute_unsat_constraints: bool = False, - ) -> None: - program_transformed, at = self._apply_assumption_transformer( - signatures=self._mus_assumption_signatures, files=files - ) - - # remove optimization statements - optr = OptimizationRemover() - program_transformed = optr.parse_string(program_transformed) - - control.add("base", [], program_transformed) - control.ground([("base", [])]) - - assumptions = at.get_assumption_literals( - control, constants=[get_constant_string(c, v, prefix="-c ") for c, v in self.argument_constants.items()] - ) - cc = CoreComputer(control, assumptions) - - max_models = int(control.configuration.solve.models) # type: ignore - print("Solving...") - - # Case: Finding a single MUS - if max_models == -1: - - def shrink_on_model(core: Sequence[int]) -> None: - _ = cc.shrink(core) - - control.solve(assumptions=list(assumptions), on_core=shrink_on_model) - - if cc.minimal is None: - print("SATISFIABLE: Instance has no MUS") - return - if len(list(cc.minimal)) == 0: - print( - "NO MUS CONTAINED: The unsatisfiability of this program is not induced by the provided assumptions" - ) - return - - mus_string = " ".join(cc.mus_to_string(cc.minimal)) - self._print_mus(mus_string) - - if compute_unsat_constraints: - self._method_unsat_constraints( - control=clingo.Control(), - files=files, - assumption_string=mus_string, - output_prefix_active=f"{COLORS['RED']}├──{COLORS['NORMAL']}", - ) - - # Case: Finding multiple MUS - if max_models >= 0: - program_unsat = False - with control.solve(assumptions=list(assumptions), yield_=True) as solve_handle: - if not solve_handle.get().satisfiable: - program_unsat = True - - if program_unsat: - n_mus = 0 - for mus in cc.get_multiple_minimal(max_mus=max_models): - n_mus += 1 - mus_string = " ".join(cc.mus_to_string(mus)) - self._print_mus(mus_string) - - if compute_unsat_constraints: - self._method_unsat_constraints( - control=clingo.Control(), - files=files, - assumption_string=mus_string, - output_prefix_active=f"{COLORS['RED']}├──{COLORS['NORMAL']}", - ) - if not n_mus: - print( - "NO MUS CONTAINED: The unsatisfiability of this program is not induced by the provided " - "assumptions" - ) - return - - def _print_unsat_constraints( - self, - unsat_constraints: Dict[int, str], - ucc: UnsatConstraintComputer, - prefix: Optional[str] = None, - ) -> None: - if prefix is None: - prefix = "" - print(f"{prefix}{BACKGROUND_COLORS['RED']} Unsat Constraints {COLORS['NORMAL']}") - for cid, constraint in unsat_constraints.items(): - location = ucc.get_constraint_location(cid) - if location is None: - warn(f"Couldn't find a corresponding file for constraint with id {cid}") - continue - relative_file_path = location.begin.filename - absolute_file_path = str(Path(relative_file_path).absolute().resolve()) - line_beginning = location.begin.line - line_end = location.end.line - line_string = ( - f"Line {line_beginning}" if line_beginning == line_end else f"Lines {line_beginning}-{line_end}" - ) - file_link = "file://" + absolute_file_path - if " " in absolute_file_path: - # If there's a space in the filename use a hyperlink - file_link = HYPERLINK_MASK.format("", file_link, file_link) - - if location is not None: - print( - f"{prefix}{COLORS['RED']}{constraint}" - f"{COLORS['GREY']} [ {file_link} ]({line_string}){COLORS['NORMAL']}" - ) - else: - print(f"{prefix}{COLORS['RED']}{constraint}{COLORS['NORMAL']}") - - def _method_unsat_constraints( - self, - control: clingo.Control, - files: List[str], - assumption_string: Optional[str] = None, - output_prefix_active: str = "", - ) -> None: - ucc = UnsatConstraintComputer(control=control) - ucc.parse_files(files) - unsat_constraints = ucc.get_unsat_constraints(assumption_string=assumption_string) - self._print_unsat_constraints(unsat_constraints, ucc=ucc, prefix=output_prefix_active) - - def _print_model( - self, - model: clingo.Model, - prefix_active: str = "", - prefix_passive: str = "", - ) -> None: - print(prefix_passive) - print( - f"{prefix_active}" - f"{BACKGROUND_COLORS['LIGHT-GREY']}{COLORS['BLACK']} Model {COLORS['NORMAL']}{BACKGROUND_COLORS['GREY']} " - f"{self._show_decisions_model_id} {COLORS['NORMAL']} " - f"{model}" - ) - # print(f"{COLORS['BLUE']}{model}{COLORS['NORMAL']}") - print(prefix_passive) - self._show_decisions_model_id += 1 - - def _method_interactive( - self, - control: clingo.Control, - files: List[str], - ) -> None: - print(control) # only for pylint - - app = ClingexplaidTextualApp(files=files, constants={}) - app.run() - - def print_model(self, model: clingo.Model, _) -> None: # type: ignore - return - - def main(self, control: clingo.Control, files: Sequence[str]) -> None: - print("clingexplaid", "version", version("clingexplaid")) - self._initialize() - - # printing the input files - if not files: - print("Reading from -") - else: - print(f"Reading from {files[0]} {'...' if len(files) > 1 else ''}") - - self.argument_constants = get_constants_from_arguments(sys.argv) - - # standard case: only one method - if len(self.methods) == 1: - method = list(self.methods)[0] - method_function = self.method_functions[method] - method_function(control, files) - # special cases where specific pipelines have to be configured - elif self.methods == {"mus", "unsat-constraints"}: - self.method_functions["mus"](control, files, compute_unsat_constraints=True) - else: - print( - f"METHOD ERROR: the combination of the methods {[f'--{m}' for m in self.methods]} is invalid. " - f"Please remove the conflicting method flags" - ) diff --git a/src/clingexplaid/cli/textual_gui.py b/src/clingexplaid/cli/textual_gui.py deleted file mode 100644 index cac4d12..0000000 --- a/src/clingexplaid/cli/textual_gui.py +++ /dev/null @@ -1,876 +0,0 @@ -""" -Module for a Command Line based GUI for clingexplaid -""" - -import argparse -import asyncio -import itertools -import re -from enum import Enum -from pathlib import Path -from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Union, cast - -import clingo -from rich.text import Text -from textual import on -from textual.app import App, ComposeResult -from textual.containers import Horizontal, HorizontalScroll, Vertical, VerticalScroll -from textual.screen import Screen -from textual.validation import Number -from textual.widget import Widget -from textual.widgets import ( - Button, - Checkbox, - Footer, - Input, - Label, - LoadingIndicator, - Select, - Static, - TabbedContent, - TabPane, - TextArea, - Tree, -) -from textual.widgets.tree import TreeNode - -from ..mus import CoreComputer -from ..propagators import SolverDecisionPropagator -from ..propagators.propagator_solver_decisions import INTERNAL_STRING, Decision -from ..transformers import AssumptionTransformer, OptimizationRemover -from ..unsat_constraints import UnsatConstraintComputer -from .textual_style import MAIN_CSS - -ACTIVE_CLASS = "active" - -COLORS = { - "BLACK": "#000000", - "GRAY-DARK": "#666666", - "GRAY": "#999999", - "GRAY-LIGHT": "#CCCCCC", - "RED": "#E53935", -} - - -class Modes(Enum): - """Representation of the Clingo-Explaid Modes""" - - MODE_MUS = "Minimal Unsatisfiable Subsets" - MODE_UNSAT_CONSTRAINTS = "Unsatisfiable Constraints" - MODE_SHOW_DECISIONS = "Show Decisions" - MODE_EXPLAIN = "Explain" - - -MODES = ( - Modes.MODE_EXPLAIN, - Modes.MODE_MUS, - Modes.MODE_UNSAT_CONSTRAINTS, - Modes.MODE_SHOW_DECISIONS, -) - - -class NoFilesException(Exception): - """ - Exception raised if a method requiring input files is called without any - """ - - -class ModelType(Enum): - """ - Types of Model that can be found (Stable Model, MUS, ...) - """ - - MODEL = 1 - UNSAT_CONSTRAINT = 2 - MUS = 3 - - -def read_file(path: Union[Path, str]) -> str: - """ - Helper function to get the contents of a file as a string. - """ - file_content = "" - with open(path, "r", encoding="utf-8") as f: - file_content = f.read() - return file_content - - -def flatten_list(ls: Optional[List[List[Any]]]) -> List[Any]: - """ - Helper function to flatten a list - """ - if ls is None: - ls = [] - return list(itertools.chain.from_iterable(ls)) - - -def parse_constants(constant_strings: List[str]) -> Dict[str, str]: - """ - Helper function to parse constants - """ - constants = {} - for const_string in constant_strings: - result = re.search(r"(^[a-zA-Z_][a-zA-Z0-9_]*)=([a-zA-Z_][a-zA-Z0-9_]*|[0-9]+)$", const_string) - if result is not None: - constants[result.group(1)] = result.group(2) - return constants - - -class SelectorWidget(Static): - """SelectorWidget Field""" - - def __init__(self, compose_widgets: List[Widget], update_value_function: Callable[[Any], str]) -> None: - super().__init__() - self.compose_widgets = compose_widgets - self.active = True - self.value = "" - self.update_value_function = update_value_function - # this is used when the value of the checkbox is set by the system to avoid the changed event callback - self.skip_checkbox_changed = False - - self.set_active_class() - - def set_active(self, active: bool) -> None: - """ - Sets the active property to the provided value while updating the Selector's style - """ - self.active = active - self.skip_checkbox_changed = True - self.query_one(Checkbox).value = active - if self.active: - self.apply_value_function() - self.set_active_class() - - def apply_value_function(self) -> None: - """ - Applies the on __init__ provided `update_value_function` to compute `SelectorWidget.value` - """ - self.value = self.update_value_function(self) - - def set_active_class(self) -> None: - """ - Sets the active class of the `SelectorWidget` according to `SelectorWidget.active` - """ - if self.active: - if ACTIVE_CLASS not in self.classes: - self.add_class(ACTIVE_CLASS) - else: - self.remove_class(ACTIVE_CLASS) - - def compose(self) -> ComposeResult: - """ - Composes the `SelectorWidget`'s components - """ - yield Checkbox(value=True) - yield from self.compose_widgets - - @on(Checkbox.Changed) - async def selector_changed(self, event: Checkbox.Changed) -> None: - """ - Callback for when the `SelectorWidget`'s Checkbox is changed. - """ - # Updating the UI to show the reasons why validation failed - if event.checkbox == self.query_one(Checkbox): - if self.skip_checkbox_changed: - self.skip_checkbox_changed = False - else: - self.active = event.checkbox.value - self.set_active_class() - await self.run_action("app.update_config") - - -class LabelInputWidget(SelectorWidget): - """LabelInputWidget Field""" - - def __init__(self, name: str, value: str, update_value_function: Callable[[SelectorWidget], str]) -> None: - super().__init__( - compose_widgets=[ - Label(name), - Input(placeholder="Value", value=value), - ], - update_value_function=update_value_function, - ) - - -class LabelWidget(SelectorWidget): - """LabelWidget Field""" - - def __init__(self, path: str, update_value_function: Callable[[SelectorWidget], str]) -> None: - super().__init__( - compose_widgets=[ - HorizontalScroll(Label(path)), - ], - update_value_function=update_value_function, - ) - - -class SelectorList(Static): - """Widget for selecting the program files""" - - def __init__(self, selectors: Optional[Iterable[Any]], classes: str = "") -> None: - super().__init__(classes=classes) - self.add_class("selectors") - if selectors is None: - selectors = [] - self.selectors = selectors - - def get_selectors(self) -> List[SelectorWidget]: - """ - Base function for getting selectors. This should be overwritten in any classes that inherit from this class. - """ - return [] - - def compose(self) -> ComposeResult: - """ - Composes the `SelectorList`'s components - """ - yield VerticalScroll( - *self.get_selectors(), - ) - - -class ConstantsWidget(SelectorList): - """List Widget for Constants""" - - def __init__(self, constants: Optional[Dict[str, str]]) -> None: - super().__init__(selectors={} if constants is None else constants) - - def get_selectors(self) -> List[SelectorWidget]: - """ - Fill the `ConstantsWidget` with `LabelInputWidget`s for each constant. - """ - return [ - LabelInputWidget(name, value, update_value_function=self.update_value) - for name, value in dict(self.selectors).items() - ] - - @staticmethod - def update_value(selector_object: SelectorWidget) -> str: - """ - Updates the value for each constant with its name and value - """ - label_string = str(selector_object.query_one(Label).renderable).strip() - input_string = str(selector_object.query_one(Input).value).strip() - return f"#const {label_string}={input_string}." - - -class FilesWidget(SelectorList): - """List Widget for Files""" - - def __init__(self, files: Optional[List[str]]) -> None: - super().__init__(selectors=[] if files is None else files) - - def get_selectors(self) -> List[SelectorWidget]: - """ - Fill the `FilesWidget` with `LabelWidget`s for each file. - """ - return [LabelWidget(name, update_value_function=self.update_value) for name in self.selectors] - - @staticmethod - def update_value(selector_object: SelectorWidget) -> str: - """ - Updates the value for each file with its name - """ - label_string = str(selector_object.query_one(Label).renderable).strip() - return label_string - - -class SignaturesWidget(SelectorList): - """List Widget for Signatures""" - - def __init__(self, signatures: Optional[List[str]]) -> None: - super().__init__(selectors=[] if signatures is None else signatures) - - def get_selectors(self) -> List[SelectorWidget]: - """ - Fill the `SignaturesWidget` with `LabelWidget`s for each signature. - """ - return [LabelWidget(name, update_value_function=self.update_value) for name in self.selectors] - - @staticmethod - def update_value(selector_object: SelectorWidget) -> str: - """ - Updates the value for each file with its name and arity - """ - label_string = str(selector_object.query_one(Label).renderable).strip() - return label_string - - -class Sidebar(Static): - """Widget for the clingexplaid sidebar""" - - def __init__( - self, - files: List[str], - constants: Optional[Dict[str, str]], - classes: str = "", - ) -> None: - super().__init__(classes=classes) - self.files = files - self.constants = {} if constants is None else constants - self.signatures = self.get_all_program_signatures() - - def get_all_program_signatures(self) -> Set[Tuple[str, int]]: - """ - Get all signatures occurring in all files provided. - """ - # This is done with grounding rn but doing a text processing would probably be more efficient for large - # programs! - ctl = clingo.Control() - for file in self.files: - ctl.load(file) - ctl.ground([("base", [])]) - return {(name, arity) for name, arity, _ in ctl.symbolic_atoms.signatures} - - def compose(self) -> ComposeResult: - """ - Composes the `Sidebar`'s components - """ - with TabbedContent(): - with TabPane("Files", id="tab-files"): - yield FilesWidget(self.files) - # with TabPane("Constants", id="tab-constants"): - # yield ConstantsWidget(self.constants) - with TabPane("Signatures", id="tab-signatures"): - sorted_signatures = list(sorted(self.signatures, key=lambda x: x[0])) - yield SignaturesWidget([INTERNAL_STRING] + [f"{name} / {arity}" for name, arity in sorted_signatures]) - - -class ControlPanel(Static): - """Widget for the clingexplaid sidebar""" - - def __init__(self, classes: str = ""): - super().__init__(classes=classes) - self.input_valid = True - - def compose(self) -> ComposeResult: - """ - Composes the `ControlPanel`'s components - """ - yield Label("Mode") - yield Select( - ((line.value, line.value) for line in MODES), - allow_blank=False, - id="mode-select", - ) - yield Label("Models") - yield Input( - placeholder="Number", - type="number", - value="1", - validate_on=["changed"], - validators=[Number(minimum=0)], - id="model-number-input", - ) - yield Static(classes="error") - yield Label("", classes="error") - yield Button("SOLVE", id="solve-button", variant="primary") - - @on(Select.Changed) - async def select_changed(self, event: Select.Changed) -> None: - """ - Callback for when the `ControlPanel`'s Mode Select is changed. - """ - if event.select == self.query_one("#mode-select"): - await self.run_action("app.update_mode") - - @on(Input.Changed) - async def input_changed(self, event: Input.Changed) -> None: - """ - Callback for when the `ControlPanel`'s Input is changed. - """ - # Updating the UI to show the reasons why validation failed - if event.input == self.query_one("#model-number-input"): - if event.validation_result is None: - return - self.input_valid = event.validation_result.is_valid - if not self.input_valid: - self.add_class("error") - first_error = event.validation_result.failure_descriptions[0] - cast(Label, self.query_one("Label.error")).update(first_error) - else: - self.remove_class("error") - cast(Label, self.query_one("Label.error")).update("") - await self.run_action("app.update_config") - - @on(Input.Submitted) - async def input_submitted(self, event: Input.Submitted) -> None: - """ - Callback for when the `ControlPanel`'s Input is submitted. - """ - if event.input == self.query_one("#model-number-input"): - if self.input_valid: - await self.run_action("app.solve") - - @on(Button.Pressed) - async def solve(self, event: Button.Pressed) -> None: - """ - Callback for when the `ControlPanel`'s Button is changed. - """ - if event.button == self.query_one("#solve-button"): - await self.run_action("app.solve") - - -class SolverTreeView(Static): - """Widget for the clingexplaid show decisions tree""" - - def __init__(self, classes: str = "") -> None: - super().__init__(classes=classes) - self.solve_tree: Tree[str] = Tree("Solver Decisions", id="explanation-tree") - - def compose(self) -> ComposeResult: - """ - Composes the `SolverTreeView`'s components - """ - self.solve_tree.root.expand() - yield self.solve_tree - yield LoadingIndicator() - - -class ClingexplaidTextualApp(App[int]): - """A textual app for a terminal GUI to use the clingexplaid functionality""" - - # pylint: disable=too-many-instance-attributes - # pylint: disable=too-many-public-methods - # pylint: disable=duplicate-code - - CSS = MAIN_CSS - - def __init__(self, files: List[str], constants: Dict[str, str]) -> None: - super().__init__() - self.files = files - self.constants = constants - self.tree_cursor: Optional[TreeNode[str]] = None - self.model_count = 0 - - self._selected_mode: Optional[str] = None - self._config_model_number = 1 - self._config_show_internal = True - self._loaded_files: Set[str] = set() - self._loaded_signatures: Set[Tuple[str, int]] = set() - - self.bind("ctrl+x", "exit", description="Exit", key_display="CTRL+X") - - def compose(self) -> ComposeResult: - """ - Composes the `ClingexplaidTextualApp`'s components - """ - yield Vertical( - ControlPanel(classes="box"), - Sidebar(files=self.files, constants=self.constants, classes="box tabs"), - id="top-cell", - ) - input_answer_set = TextArea() - input_answer_set.border_title = "Answer Set" - input_query_atoms = TextArea() - input_query_atoms.border_title = "Query Atoms" - - yield Vertical( - VerticalScroll(SolverTreeView(), classes="box"), - Horizontal( - input_answer_set, - input_query_atoms, - classes="box", - id="explanation-inputs", - ), - id="content", - ) - yield Label(id="debug") - yield Footer() - - def action_exit(self) -> None: - """ - Action to exit the textual application - """ - self.exit(0) - - async def on_model(self, model: List[str]) -> None: - """ - Callback for when clingo finds a model. - """ - self.model_count += 1 - if self.tree_cursor is None: - return - await self.add_model_node(" ".join(model), ModelType.MODEL) - # add some small sleep time to make ux seem more interactive - await asyncio.sleep(0.01) - - def on_propagate(self, decisions: List[Union[Decision, List[Decision]]]) -> None: - """ - Callback for the registered propagator does a propagate step. - """ - if self.tree_cursor is None: - return - for element in decisions: - if isinstance(element, list): - for literal in element: - if literal.matches_any(self._loaded_signatures, show_internal=self._config_show_internal): - entailment = self.tree_cursor.add_leaf(str(literal)).expand() - cast(Text, entailment.label).stylize(COLORS["GRAY-DARK"]) - else: - new_node = self.tree_cursor.add(str(element)) - new_node.expand() - self.tree_cursor = new_node - - def on_undo(self) -> None: - """ - Callback for the registered propagator does an undo step. - """ - if self.tree_cursor is None: - return - undo = self.tree_cursor.add_leaf(f"UNDO {self.tree_cursor.label}") - cast(Text, undo.label).stylize(COLORS["RED"]) - self.tree_cursor = self.tree_cursor.parent - - async def action_update_config(self) -> None: - """ - Action to update the solving config - """ - self.refresh_bindings() - - # update model number - model_number_input = cast(Input, self.query_one("#model-number-input")) - model_number = int(model_number_input.value) - self._config_model_number = model_number - - # update loaded files - files_widget = self.query_one(FilesWidget) - files = set() - for selector in files_widget.query(SelectorWidget): - selector.apply_value_function() - if selector.active: - files.add(selector.value) - self._loaded_files = files - - # update program signatures - signatures_widget = self.query_one(SignaturesWidget) - signature_strings = set() - for selector in signatures_widget.query(SelectorWidget): - selector.apply_value_function() - if selector.active: - signature_strings.add(selector.value) - signatures = set() - self._config_show_internal = False - for signature_string in signature_strings: - if signature_string.startswith(INTERNAL_STRING): - self._config_show_internal = True - else: - name, arity = signature_string.split(" / ") - signatures.add((name, int(arity))) - self._loaded_signatures = signatures - - async def action_mode_mus(self) -> None: - """ - Action for MUS Mode - """ - if not self._loaded_files: - raise NoFilesException("No files are loaded so there is no MUS to be found") - - ctl = clingo.Control() - - # transform matching facts to choices to allow assumptions - at = AssumptionTransformer(signatures=self._loaded_signatures) - program_transformed = at.parse_files(list(self._loaded_files)) - - # remove optimization statements - opt_rm = OptimizationRemover() - program_no_opt = opt_rm.parse_string(program_transformed) - - ctl.add("base", [], program_no_opt) - ctl.ground([("base", [])]) - - # get assumption set - assumptions = at.get_assumption_literals(ctl) - # FIX: add program constants - # get_constant_string(c, v, prefix="-c ") for c, v in self.argument_constants.items() - - cc = CoreComputer(ctl, assumptions) - - # check if the program is unsat - program_unsat = False - with ctl.solve(assumptions=list(assumptions), yield_=True) as solve_handle: - if not solve_handle.get().satisfiable: - program_unsat = True - - tree_cursor = await self.get_tree_cursor() - if not program_unsat: - tree_cursor.add_leaf("SATISFIABLE PROGRAM") - return - - for mus in cc.get_multiple_minimal(max_mus=self._config_model_number): - self.model_count += 1 - mus_string = " ".join(cc.mus_to_string(mus)) - await self.add_model_node(mus_string, ModelType.MUS) - - if not self.model_count: - tree_cursor.add_leaf( - "NO MUS CONTAINED: The unsatisfiability of this program is not induced by the provided assumptions" - ) - - async def action_mode_unsat_constraints(self) -> None: - """ - Action for Unsat Constraints Mode - """ - control = clingo.Control() - ucc = UnsatConstraintComputer(control=control) - ucc.parse_files(list(self._loaded_files)) - unsat_constraints = ucc.get_unsat_constraints() - - for cid, constraint in unsat_constraints.items(): - self.model_count += 1 - location = ucc.get_constraint_location(cid) - if location is None: - await self.add_model_node("UNKNOWN CONSTRAINT LOCATION", ModelType.UNSAT_CONSTRAINT) - continue - relative_file_path = location.begin.filename - absolute_file_path = str(Path(relative_file_path).absolute().resolve()) - line_beginning = location.begin.line - line_end = location.end.line - constraint_string = constraint - line_string = ( - f" (line {line_beginning})" if line_beginning == line_end else f" (lines {line_beginning}-{line_end})" - ) - constraint_string += line_string - node = await self.add_model_node(constraint_string, ModelType.UNSAT_CONSTRAINT) - if node is not None: - cast(Text, node.label).stylize(COLORS["GRAY-DARK"], -len(line_string)) - node.expand() - node.add_leaf(f"File: {absolute_file_path}") - - async def action_mode_show_decisions(self) -> None: - """ - Action for Show Decisions Mode - """ - sdp = SolverDecisionPropagator( - callback_propagate=self.on_propagate, - callback_undo=self.on_undo, - ) - ctl = clingo.Control(f"{self._config_model_number}") - ctl.register_propagator(sdp) - for file in self._loaded_files: - ctl.load(file) - if not self._loaded_files: - ctl.add("base", [], "") - ctl.ground([("base", [])]) - - exhausted = False - - with ctl.solve(yield_=True) as solver_handle: - while not exhausted: - result = solver_handle.get() - if result.satisfiable: - model = solver_handle.model() - if model is None: - break - await self.on_model([str(a) for a in model.symbols(atoms=True)]) - exhausted = result.exhausted - if not exhausted: - solver_handle.resume() - - await self.reset_tree_cursor() - end_string = "SAT" if self.model_count > 0 else "UNSAT" - if self.tree_cursor is None: - return - self.tree_cursor.add(end_string) - - async def action_mode_explain(self) -> None: - """Action for Explain Mode""" - # Add implementation - - async def action_solve(self) -> None: - """ - Action to exit the textual application - """ - self.refresh_bindings() - - tree_view = self.query_one(SolverTreeView) - tree = tree_view.solve_tree - await self.reset_solve_tree(str(tree.root.label)) - - solve_button = self.query_one("#solve-button") - - self.model_count = 0 - - solve_button.disabled = True - tree_view.add_class("solving") - - match self._selected_mode: - case Modes.MODE_MUS.value: - try: - await self.action_mode_mus() - except NoFilesException as e: - tree_cursor = await self.get_tree_cursor() - tree_cursor.add_leaf(e.args[0]) - case Modes.MODE_UNSAT_CONSTRAINTS.value: - await self.action_mode_unsat_constraints() - case Modes.MODE_SHOW_DECISIONS.value: - await self.action_mode_show_decisions() - case Modes.MODE_EXPLAIN.value: - await self.action_mode_explain() - - tree_view.remove_class("solving") - solve_button.disabled = False - - await self.reset_tree_cursor() - - async def action_update_mode(self) -> None: - """ - Action that updates the currently selected mode - """ - selected_mode = cast(Select[str], self.query_one("#mode-select")).value - self._selected_mode = str(selected_mode) - match selected_mode: - case Modes.MODE_MUS.value: - self.set_mode_class(self.query_one(Screen), "mode-mus") - await self.set_all_selectors(self.query_one(SignaturesWidget), False) - await self.reset_solve_tree("Minimal Unsatisfiable Subsets") - await self.update_selector_tabs(disabled=["tab-constants"]) - case Modes.MODE_UNSAT_CONSTRAINTS.value: - self.set_mode_class(self.query_one(Screen), "mode-unsat-constraints") - await self.reset_solve_tree("Unsatisfiable Constraints") - await self.update_selector_tabs(disabled=["tab-constants", "tab-signatures"]) - case Modes.MODE_SHOW_DECISIONS.value: - self.set_mode_class(self.query_one(Screen), "mode-show-decisions") - await self.set_all_selectors(self.query_one(SignaturesWidget), True) - await self.reset_solve_tree("Solver Decisions") - await self.update_selector_tabs(disabled=["tab-constants"]) - case Modes.MODE_EXPLAIN.value: - self.set_mode_class(self.query_one(Screen), "mode-explain") - await self.reset_solve_tree("Explanation") - - async def action_debug(self, msg: str) -> None: - """ - Action to add a leaf with custom message to the solve tree for debugging purposes - """ - tree_cursor = await self.get_tree_cursor() - tree_cursor.add_leaf(f"DEBUG: {msg}") - - @staticmethod - def set_mode_class(widget: Widget, mode_class: str) -> None: - """ - Sets the provided mode class on the provided widget - """ - mode_classes = [c for c in widget.classes if c.startswith("mode-")] - for c in mode_classes: - widget.remove_class(c) - widget.add_class(mode_class) - - @staticmethod - async def set_all_selectors(parent: Widget, value: bool) -> None: - """ - Sets the values of all SelectorWidgets under the provided parent to value - """ - for selector in parent.query(SelectorWidget): - selector.set_active(value) - selector.skip_checkbox_changed = False - - async def add_model_node(self, value: str, model_type: ModelType) -> Optional[TreeNode[str]]: - """ - Adds a model node of type model_type to the solver tree - """ - match model_type: - case ModelType.MODEL: - color_fg, color_bg_1, color_bg_2 = (COLORS["BLACK"], COLORS["GRAY-LIGHT"], COLORS["GRAY"]) - case ModelType.UNSAT_CONSTRAINT: - color_fg, color_bg_1, color_bg_2 = ("#f27573", "#7c1313", "#610f0f") - case ModelType.MUS: - color_fg, color_bg_1, color_bg_2 = ("#66bdff", "#004578", "#003761") - case _: - raise ValueError("Model type not supported") - - model_name = model_type.name.replace("_", " ") - tree_cursor = await self.get_tree_cursor() - model_node = tree_cursor.add(f" {model_name} {self.model_count} {value}") - cast(Text, model_node.label).stylize(f"{color_fg} on {color_bg_1}", 0, len(model_name) + 2) - cast(Text, model_node.label).stylize( - f"{color_fg} on {color_bg_2}", len(model_name) + 2, len(model_name) + 4 + len(str(self.model_count)) - ) - return model_node - - async def reset_tree_cursor(self) -> None: - """ - Resets the tree cursor to the SolverTree root - """ - tree_view = self.query_one(SolverTreeView) - tree = tree_view.solve_tree - self.tree_cursor = tree.root - - async def reset_solve_tree(self, new_root_name: str) -> None: - """ - Resets the SolverTree - """ - tree_view = self.query_one(SolverTreeView) - tree = tree_view.solve_tree - tree.reset(new_root_name) - self.tree_cursor = tree.root - - async def get_tree_cursor(self) -> TreeNode[str]: - """ - Returns the current tree cursor or initializes it if it is set to None - """ - if self.tree_cursor is None: - self.tree_cursor = self.query_one(SolverTreeView).solve_tree.root - return self.tree_cursor - - async def update_selector_tabs(self, disabled: Optional[Iterable[str]] = None) -> None: - """ - Updates the selector tabs in the Sidebar depending on the provided `disabled` list. - """ - if disabled is None: - disabled = [] - sidebar = self.query_one(Sidebar) - tabbed_content = sidebar.query_one(TabbedContent) - tabs = sidebar.query(TabPane) - for tab in tabs: - if tab.id is None: - continue - if tab.id in disabled: - tab.disabled = True - else: - tab.disabled = False - if tabbed_content.active in disabled: - tabbed_content.active = str(tabs[0].id) - - def check_action(self, action: str, parameters: tuple[object, ...]) -> bool | None: - """ - Check if any action may run - """ - return True - - -def textual_main() -> None: - """ - Main function for the clingo-explaid textual app. This function includes a dedicated ArgumentParser - """ - - parser = argparse.ArgumentParser(prog="clingexplaid", description="What the program does", epilog="Epilog Text") - parser.add_argument( - "files", - type=str, - nargs="+", - action="append", - help="All logic program files", - ) - parser.add_argument( - "-c", - "--const", - type=str, - nargs="*", - action="append", - help="Specifies a clingo constant value", - ) - parser.add_argument( - "-d", - "--decision-signature", - type=str, - nargs="*", - action="append", - help="Defines shown signatures in solver decision tree", - ) - args = parser.parse_args() - - app = ClingexplaidTextualApp( - files=list(set(flatten_list(args.files))), - constants=parse_constants(flatten_list(args.const)), - ) - app.run() diff --git a/src/clingexplaid/cli/textual_style.py b/src/clingexplaid/cli/textual_style.py deleted file mode 100644 index a64050b..0000000 --- a/src/clingexplaid/cli/textual_style.py +++ /dev/null @@ -1,167 +0,0 @@ -""" -Module containing TCSS style strings for the textual TUI -""" - -MAIN_CSS = """ -Screen { - layout: grid; - grid-size: 2 2; - grid-columns: 1fr 2fr; - grid-rows: 1fr auto; -} - -#debug{ - column-span: 2; -} - -.box { - height: 100%; - border: round #455A64; - padding: 1; -} - -.box.tabs{ - padding: 0; -} - -#top-cell { - layout: grid; - grid-size: 1; - grid-rows: auto 1fr; -} - -ControlPanel { - layout: grid; - grid-size: 2; - grid-columns: auto 1fr; - grid-gutter: 1; -} - -ControlPanel Input{ - width: 100%; -} - -ControlPanel Label { - padding: 1 2; - background: #263238; - width: 100%; -} - -ControlPanel Label.error{ - border: tall rgb(235,64,52); - background: rgba(235,64,52,0.2); - padding: 0 2; - color: rgb(235,64,52); -} - -ControlPanel .error{ - display: none; -} - -ControlPanel.error .error{ - display: block -} - -ControlPanel #solve-button{ - column-span: 2; - width: 100%; -} - -ControlPanel.error #solve-button{ - display: none; -} - -ControlPanel #solve-button{ - display: block; - width: 100%; -} - -.selectors{ - background: #000; -} - -SelectorWidget{ - layout: grid; - grid-size: 2; - grid-columns: auto 1fr; -} - -LabelInputWidget{ - layout: grid; - grid-size: 3; - grid-columns: auto 1fr 1fr; -} - -SelectorWidget{ - background: transparent; -} - -SelectorWidget.active{ - background: $primary-darken-3; -} - -SelectorWidget Label{ - padding: 1; -} - -SelectorWidget Checkbox{ - background: transparent; -} - -SelectorWidget Input{ - border: none; - padding: 1 2; -} - -SelectorWidget HorizontalScroll{ - height: auto; - background: transparent; -} - -SolverTreeView{ - content-align: center middle; -} - -SolverTreeView Tree{ - display: block; - padding: 1 2; - background: #000; -} - -SolverTreeView LoadingIndicator{ - height: auto; - padding: 1; - background: #000; - display: none; -} - -SolverTreeView.solving LoadingIndicator{ - display: block; -} - -#content{ - layout: grid; - grid-size: 1 2; - grid-rows: 1fr auto; -} - -#explanation-inputs{ - layout: grid; - grid-size: 2 1; - grid-columns: 2fr 1fr; - display: none; -} - -.mode-explain #explanation-inputs{ - display: block; -} - -#explanation-inputs TextArea{ - border: tall #455A64; - background: #000; - border-title-color: #FFF; - border-title-background: #000; - min-height: 7; - max-height: 7; -} -"""