Package: clyngor
Handy python wrapper aroundPotassco's ClingoASP solver.ExampleClyngor offers multiple interfaces. The followings are all equivalent.
(they search forformal concepts)fromclyngorimportASP,solveanswers=ASP("""rel(a,(c;d)). rel(b,(d;e)).obj(X):- rel(X,_) ; rel(X,Y): att(Y).att(Y):- rel(_,Y) ; rel(X,Y): obj(X).:- not obj(X):obj(X).:- not att(Y):att(Y).""")foranswerinanswers:print(answer)The same, but with the lower level function expecting files:answers=solve(inline="""rel(a,(c;d)). rel(b,(d;e)).obj(X):- rel(X,_) ; rel(X,Y): att(Y).att(Y):- rel(_,Y) ; rel(X,Y): obj(X).:- not obj(X):obj(X).:- not att(Y):att(Y).""")More traditional interface, using file containing the ASP source code:answers=solve('concepts.lp'):# also accepts an iterable of fileMore examples are available inthe unit tests.ChainingOnce you get your answers, clyngor allows you to specify
the answer sets format using builtin methods:foranswerinanswers.by_predicate.first_arg_only:print('{'+','.join(answer['obj'])+'} × {'+','.join(answer['att'])+'}')And if you need apyasp-likeinterface:foranswerinanswers.as_pyasp:print('{'+','.join(a.args()[0]forainanswerifa.predicate=='obj')+'} × {'+','.join(a.args()[0]forainanswerifa.predicate=='att')+'}')Currently, there is only one way to see all chaining operator available:the source code of the Answers object.
(orhelp(clyngor.Answers))Official Python APIIf the used version of clingo is compiled with python, you can put python code into your ASP code as usual.
But if you also have theclingo packageinstalled and importable, clyngor can use it for various tasks.Using the official API leads to the following changes :both robust and quick parsing, instead of the simple vs slow methodsome options are not supported : constants,time-limit, parsing error handling,decoupled grounding/solvingYou can activate the use of the clingo module by calling onceclyngor.activate_clingo_module()or callingclyngor.solvewith argumentuse_clingo_moduleset toTrue.Python embeddingFor users putting some python in their ASP, clyngor may help.
The only condition is to have clingo compiled with python support,
and having clyngor installed for the python used by clingo.Easy ASP functorsClyngor providesconverted_typesfunction,
allowing one to avoid boilerplate code based on type annotation when
calling python from inside ASP code.Example (seetestsfor more):#script(python)fromclyngor.upapiimportconverted_types@converted_typesdeff(a:str,b:int):yielda*byieldlen(a)*b#end.p(X):-(X)=@f("hello",2).p(X):-(X)=@f(1,2).%ignored,becausetypesdonotmatchWithoutconverted_types, user have to ensure thatfis a function returning a list,
and that arguments are of the expected type.Note that theincoming clingo versionis leading to that flexibility regarding returned values.Generalist propagatorsPropagators are presented inthis paper. They are basically active
observers of the solving process, able for instance to modify truth assignment
and invalidate models.As shown inclyngor/test/test_propagator_class.py,
a high-level propagator class built on top of the official API is available, useful in many typical use-cases.Python constraint propagatorsAs shown inexamples/pyconstraint.lp,
clyngor also exposes some helpers for users wanting to create propagators
that implement an ASP constraint, but written in Python:#script(python)fromclyngorimportConstraint,VariableasV,Main# Build the constraint on atom bdefformula(inputs)->bool:returninputs['b',(2,)]constraint=Constraint(formula,{('b',(V,))})# regular main function that register given propagator.main=Main(propagators=constraint)#end.%ASPpart,computing3models,b(1),b(2)andb(3).1{b(1..3)}1.DecodersAn idea coming from theJSON decoders, allowing user to specify how to decode/encode custom objects in JSON.
With clyngor, you can do something alike for ASP (though very basic and only from ASP to Python):importclyngor,itertoolsASP_LIST_CONCEPTS="""% one model contains all conceptsconcept(0).extent(0,(a;b)). intent(0,(c;d)).concept(1).extent(1,(b;e)). intent(1,(f;g)).concept(2).extent(2,b). intent(2,(c;d;f;g))."""classConcept:"Decoder of concepts in ASP output"def__init__(self,concept:1,extent:all,intent:all):self.id=int(concept[0])self.extent=frozenset(argfornb,arginextentifnb==self.id)self.intent=frozenset(argfornb,arginintentifnb==self.id)def__str__(self):returnf"<{self.id}:{{{','.join(sorted(self.extent))}}}×{{{','.join(sorted(self.intent))}}}>"objects=clyngor.decode(inline=ASP_LIST_CONCEPTS,decoders=[Concept])print('\t'.join(map(str,objects)))This code will print something like:<2: {b} × {c,d,f,g}>	<0: {a,b} × {c,d}>	<1: {b,e} × {f,g}>Note the use of annotations to declare that eachconceptmust be associated to one instance,
and that allextentandintentmust be sent to constructor for each object.Seetestsfor complete API example.Remaining features for a good decoder support:encoding: try to more-or-less automatically build the python to ASP compilermore available annotations, for instance(3, 5)(to ask for between 3 and 5 atoms to be associated with the instance), orany(exact meaning has to be found)allow to raise an InvalidDecoder exception during decoder instanciation to get the instance discardedAlternativesClyngor is basically the total rewriting ofpyasp, which is now abandoned.For an ORM approach, give a try toclorm.Installationpip install clyngorYou must haveclingoin your path. Depending on your OS, it might be done with a system installation,
or throughdownloadingand (compilation and) manual installation.You may also want to install thepython clingo module,
which isan optional dependancy.TipsCareful parsingBy default, clyngor uses a very simple parser (yeah,str.split) in order to achieve time efficiency in most time.
However, when asked to compute a particular output format (likeparse_args) or an explicitelycareful parsing,
clyngor will use a much more robust parser (made with anarpeggiogrammar).Import/exportSee theutilsmoduleand itstests,
which provides high level routines to save and load answer sets.Define the path to clingo binaryimportclyngorclyngor.CLINGO_BIN_PATH='path/to/clingo'Note that it will be the very first parameter tosubprocess.Popen.
Thesolvefunction also support theclingo_bin_pathparameter.The third solution is to use the decoratorwith_clingo_bin, which modify the global variable
during the execution of a specific function:importclyngor@clyngor.with_clingo_bin('clingo454')defsequence():...clyngor.solve(...)# will use clingo454, not clingo, unless clingo_bin_path is given...clyngor.solveparametersThesolvefunctions allow to pass explicitely some parameters to clingo
(including number of models to yield, time-limit, and constants).
Using theoptionsparameter is just fine, but with the explicit parameters some verifications
are made against data (mostly about type).Therefore, the two followings are equivalent ; but the first is more readable and will crash earlier with a better error message ifnis not valid:solve('file.lp',nb_model=n)solve('file.lp',options='-n '+str(n))FAQDinopython support ?No.Contributions ?Yes.Why clyngor ?No, it's pronouncedclyngor.Explain me again the thing with the official moduleClyngor was designed to not require the official module, because it required a manual compilation and installation of clingo.
However, because of the obvious interest in features and performances,
the official module can be used by clyngor if it is available.Further ideastimeoutin addition to time-limitASP source code debugging generator (started inclyngor-parser)What is clyngor used for ?bioinformatics, to encode biological pathway logic inpathmodelandMenetools, and forcommunity detection.mathematics, to encode someFCA-related task such asAOC-poset generationorconcept search, andgraph compressionorgraph manipulationin the context of graph theory.visualization, withDraco, a formalization of visualization design knowledge as constraints, andbiseau, an ASP-to-graph compiler.web applications, fora sudoku solver made with ASP.Changelog0.4.0 (todo)seefurther ideas0.3.28706747045: handle default negation on atoms, by keeping the prefixed dashd4830ad52: fix an import error0.3.2580245b2a7: remove f-strings for 3.4 and 3.5 compat.6efdb6ab0: fix combination of .as_pyasp and .parse_args, where atoms in args were not transformed as pyasp Atom objects.fe4107573: correctly parse atoms starting with underscores.d6507f17d: careful parsing is automatically set when answer setobviously needs it.f2c65e8ae: fixed bug when using clingo module and.int_not_parsed.0.3.24f92248e91:#show 3.and#show "hello !".are now handled31375774c: when using clingo module, the models contains only the output atoms, not everything (thank you Arnaud)cc6021797: support for.with_answer_number, giving model, optimization, optimality and answer numberc0c090c34: parsing and string reproduction of nested atoms such asa((a("g(2,3)",(2)),))is now correctly handled and tested1840c36e3: fix themodels.commandoutput when clingo module is used2679d26a9: optimize memory usage ofopt_models_from_clyngor_answersby using yield and answer number, but is now a generator and loses (the useless)repeated_optimaloption0.3.20fix#7improve testing cover, fix warning in recent versions of pytestmore robust options parsing when solving with clingo module0.3.19fix#160.3.18TermSet bugfixTermSet.addto add atoms to the TermSetTermSet.unionto generate the union of multiple TermSet instances0.3.17support for decoupled grounding and solving, as shown indedicated examplenew parameterreturn_raw_outputfor clyngor.solve, allowing to get stdout/stderr without treatmentsnew exampleshowing how to retrieve all optimal models using clyngor, and…… the defined functionopt_models_from_clyngor_answersis now embedded in clyngor API0.3.16support for.by_arity, equivalent to.by_predicatebut with predicate and aritydecoratorwith_clingo_bin, changing clingo binary path for encapsulated functionsupport for.with_optimality, giving optimization and optimality along with the model0.3.14decoders support, seeclyngor.decoderanddoc0.3.10support for.discard_quotesoption (thanks to ArnaudBelcour)bugfix:.atom_as_stringand.first_arg_onlycollisionbugfix: more robust tempfile deletion and closing managementdemonstrationof the non-working Constraint type implementationbeforepredicat to know if python/lua are available with used clingo binaryeasy interface for most use cases using type hint for embedded pythoneasy python constraints in ASP with Constraint typeadd support for propagatorsadd support for clingo official python modulefrom pyasp to clyngorIf you have a project that makes use of pyasp, but need clingo instead of gringo+clasp, one way to go is to use clyngor instead.Here was my old code:frompyaspimportaspdefsolving(comp,graph):programs=[comp,graph]clasp_options=['--opt-mode=optN','--parallel-mode=4','--project']solver=asp.Gringo4Clasp(clasp_options=clasp_options)print("solver run as: `clingo{}{}`".format(' '.join(programs),clasp_options))at_least_one_solution=Falseforanswersetinsolver.run(programs,collapseAtoms=False):yieldanswersetdeffind_direct_inclusions(model)->dict:programs=[ASP_SRC_INCLUSION]solver=asp.Gringo4Clasp()add_atoms=''.join(str(atom)+'.'foratominmodel)answers=tuple(solver.run(programs,collapseAtoms=False,additionalProgramText=add_atoms))returnanswersAnd here is the version using clyngor, that pass the exact same unit tests:importclyngordefsolving(comp,graph):programs=[comp,graph]clasp_options='--opt-mode=optN','--parallel-mode=4','--project'answers=clyngor.solve(programs,options=clasp_options)print("solver run as: `{}`".format(answers.command))foranswersetinanswers.as_pyasp.parse_args.int_not_parsed:yieldanswersetdeffind_direct_inclusions(model)->dict:programs=[ASP_SRC_INCLUSION]add_atoms=''.join(str(atom)+'.'foratominmodel)answers=tuple(clyngor.solve(programs,inline=add_atoms).as_pyasp.parse_args)returnanswersThanksToArnaud Belcourfor his works and frequent feedbacks.ToDomoritzfor his questions and feedbacks.