Commit 13e89b17 authored by Sylvain Soliman's avatar Sylvain Soliman

Merge branch 'release/4.4.7'

parents 86431327 36671deb
......@@ -54,8 +54,8 @@ KERNEL_DIR=$(JUPYTER_DIR)/kernel/biocham_kernel
WORKFLOWS_DIR=$(JUPYTER_DIR)/guinbextension/src/config/workflows
# NOTEBOOKS=$(shell find . -type f -name '*.ipynb' -print)
NOTEBOOKS=library/examples/doctor_in_the_cell/diagnosis.ipynb library/examples/tutorial/tutorial17cmsb.ipynb
REFDIR=nbhtml
NOTEBOOKS=library/examples/C2-19-Biochemical-Programming/TD1_lotka_volterra.ipynb library/examples/doctor_in_the_cell/diagnosis.ipynb
REFDIR=nbrefs
all: biocham biocham_debug quick doc/index.html pldoc install_kernel install_gui
......@@ -114,6 +114,7 @@ unit_tests: biocham_debug
test: biocham_debug
echo "flag(slow_test, _, true), run_tests_and_halt." | ./biocham_debug
$(MAKE) jupyter_tests
$(MAKE) refs
jupyter_tests: install_kernel
PATH=$(CURDIR):$(PATH) jupyter nbconvert --execute --stdout library/examples/cmsb_2017/sigmoids.ipynb > /dev/null
......@@ -148,7 +149,7 @@ $(KERNEL_DIR)/commands.js: doc/commands.js
cp -f $< $@
# only static .bc files in that dir
$(WORKFLOWS_DIR)/workflows.js:
$(WORKFLOWS_DIR)/workflows.js:
# for file in $(WORKFLOWS_DIR)/nb/*.ipynb ; do jupyter nbconvert --to script $$file ; done
python3 $(WORKFLOWS_DIR)/make_workflows.py -i $(WORKFLOWS_DIR)/nb/ -o $@
......@@ -211,15 +212,27 @@ release:
git flow release finish "$$version" && \
git push --tags
refs: $(NOTEBOOKS) biocham
mkdir -p $(REFDIR)
jupyter nbconvert --ExecutePreprocessor.timeout=None \
--to html --output-dir $(REFDIR) --execute $(NOTEBOOKS)
for file in $(NOTEBOOKS) ; do \
out=$(REFDIR)/$$(basename $${file%%ipynb}html) && \
# if necessary convert a notebook to a biocham script
%.bc: %.ipynb
jupyter nbconvert --to script $<
refs: $(NOTEBOOKS:.ipynb=.bc) biocham
@mkdir -p $(REFDIR)
@for file in $(NOTEBOOKS) ; do \
out=$(REFDIR)/$$(basename $${file%%ipynb}out) && \
result=0 && \
printf "$$out " && \
echo "load($${file%%ipynb}bc). quit." | ./biocham --jupyter 2>&1 | \
grep -v '^\(Biocham [4-9]\.[0-9]\|Copyright (C)\|Time\|Robustness degree\|Best satisfaction degree\|\[.*\] parameter\|Stopping reason\)' > $$out && \
rm -f $$(dirname $$file)/graph?.png && \
rm -f $$(dirname $$file)/ode.tex && \
rm -f $$(dirname $$file)/plot-?.csv{,X} && \
if [ -f $$out.ref ] ; then \
diff -q $$out $$out.ref || false ; break ; \
diff -q $$out $$out.ref >/dev/null && printf "\e[1;32m✔\e[0m\n" || \
{ printf "\e[1;31m✘\e[0m\n" && result=1 && break ; } \
else \
echo "Saving $$out to $$out.ref" && \
cp $$out $$out.ref ; \
fi ; \
done
done ; \
[ "$$result" -eq 0 ]
......@@ -8,7 +8,7 @@
about/0
]).
version('4.4.6').
version('4.4.7').
copyright(
'Copyright (C) 2003-2020 Inria, EPI Lifeware, Saclay-Île de France, France'
......
FROM registry.gitlab.inria.fr/lifeware/biocham:v4.4.6
FROM registry.gitlab.inria.fr/lifeware/biocham:v4.4.7
......@@ -93,6 +93,9 @@ initialize :-
set_prolog_flag(allow_variable_name_as_functor, true),
prolog_history(enable),
set_counter(item_id, 0),
set_counter(model, 0),
set_counter(molecule_id, 0),
set_counter(parameter_id, 1),
set_plot_driver(gnu_plot),
set_image_viewer_driver(open_file),
nb_setval(ode_viewer, inline),
......
{
"name": "gui",
"version": "4.4.6",
"version": "4.4.7",
"description": "biocham gui in jupyter notebook",
"main": "src/index.js",
"scripts": {
......
......@@ -28,6 +28,7 @@ commands = [
"change_parameter_to_variable",
"check_conservations",
"check_ctl",
"check_ltl",
"check_multistability",
"check_oscillations",
"cleanup_ctl",
......
div.output_subarea pre {
white-space: pre;
}
#gui_panel {
display: none;
visibility: visible;
......
"""Example magic"""
__version__ = '4.4.6'
__version__ = '4.4.7'
......@@ -127,41 +127,38 @@ foltl_predicate(E = F) :-
arithmetic_expression(E),
arithmetic_expression(F).
foltl_predicate(E <> F) :-
arithmetic_expression(E),
arithmetic_expression(F).
foltl_predicate(E << F) :-
arithmetic_expression(E),
arithmetic_expression(F).
foltl_predicate(E >> F) :-
arithmetic_expression(E),
arithmetic_expression(F).
foltl_predicate(E < F) :-
arithmetic_expression(E),
arithmetic_expression(F).
foltl_predicate(E <= F) :-
arithmetic_expression(E),
arithmetic_expression(F).
foltl_predicate(E > F) :-
arithmetic_expression(E),
arithmetic_expression(F).
foltl_predicate(E >= F) :-
arithmetic_expression(E),
arithmetic_expression(F).
% for the Boolean case
foltl_predicate(E) :-
name(E).
:- grammar(time_value).
......
......@@ -39,6 +39,7 @@ Rq: The distinction between pivp_string and pivp_list is not always obvious.
monomial/1,
pode/1,
pivp/1,
reduction_methods/1,
%API
format_pivp/3
]).
......@@ -55,14 +56,17 @@ Rq: The distinction between pivp_string and pivp_list is not always obvious.
:- doc('The compilation from mathematical expression is restricted to some standard functions and simple operations using a functional notation where \\texttt{id} represents the operand.').
:- doc('The compilation from PIVPs is implemented in full generality').
:- doc('The compilation from PIVPs is implemented with full generality.').
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Main Tools of the module %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
:- doc('The option for binomial reduction restricts the synthesis of reactions with at most two reactants (the default is not).').
:- doc('The option for binomial reduction restricts the synthesis to reactions with at
most two reactants (the default is not, since this process may be costly).
Two methods are available to perform this a posteriori reduction: natively
or using an external SAT solver.').
:- initial(option(binomial_reduction: no)).
......@@ -70,6 +74,7 @@ Rq: The distinction between pivp_string and pivp_list is not always obvious.
:- initial(option(lazy_negatives: yes)).
%! compile_from_expression(+Expr, +Output)
%
% biocham command
......@@ -91,7 +96,7 @@ compile_from_expression(Expr, Output) :-
'),
option(
binomial_reduction,
yesno,
reduction_methods,
_Reduction,
'Determine if the binomial reduction for synthesizing reactions with at most two reactants has to be performed'
),
......@@ -132,7 +137,7 @@ compile_from_expression(Expr, Input, Output) :-
'),
option(
binomial_reduction,
yesno,
reduction_methods,
_Reduction,
'Determine if the binomial reduction for synthesizing reactions with at most two reactants has to be performed'
),
......@@ -196,7 +201,7 @@ compile_from_expression(Expr, Input, Output) :-
:- devdoc('\\begin{todo} share common subexpression\\end{todo}').
:- doc('One can also compile real valued functions defined as solutions of Polynomial Initial Value Problems (PIVP), i.e. solutions of polynomial differential equations, using the following syntax:').
:- doc('One can also compile real valued functions defined as solutions of Polynomial Initial Value Problems (PIVP), i.e. solutions of polynomial differential equations with initial values defined by polynomials of the input. PIVPs are written with the following grammar:').
% Definition of the various grammars used in this module:
......@@ -275,6 +280,18 @@ monomial(X) :-
name(X).
%! reduction_methods(?Expression) is det.
%
% Available reduction methods.
:- grammar(reduction_methods).
reduction_methods(no).
reduction_methods(native).
reduction_methods(sat).
%! compile_from_pivp(+PIVP_string, +Output)
%
% biocham command
......@@ -292,17 +309,44 @@ compile_from_pivp(PIVP, Output) :-
type(PIVP, pivp),
type(Output, name),
doc('
compiles a PIVP into a CRN (with initial concentration values) that computes the projection on one variable Output as a function of time, of the multivariate function f(t) solution of the PIVP.
creates a CRN that immplements a function of time, specified by the variable "Output" of the solution of the PIVP.
'),
option(
binomial_reduction,
yesno,
reduction_methods,
_Reduction,
'Determine if the binomial reduction has to be performed'
'Determines if the binomial reduction has to be performed'
),
sort_output(PIVP, Output, PIVP_sorted),
compile_from_pivp(PIVP_sorted, time, Output).
%:- doc('
% \\begin{example} Compilation of a simple oscillator with 2 species (Lotka-Volterra)\n
%').
%%:- biocham(option(lazy_negatives:yes)).
%:- biocham(compile_from_pivp((0.5,(d(x)/dt= x - x*y));(0.25,(d(y)/dt= x*y - 0.5*y)), x)).
%:- biocham(list_model).
%:- biocham(numerical_simulation(time:10)). % method:msbdf
%:- biocham(plot(show:{'x','y'})).
%:- doc('
% \\end{example}
% ').
:- doc('
\\begin{example} Compilation of the Hill function of order 2 as a function of time, $h(t)=t^2/(1+t^2)$ \n
').
%:- biocham(option(lazy_negatives:yes)).
:- biocham(option(binomial_reduction:native)).
:- biocham(compile_from_pivp((0.0,d(h)/dt= 2*n^2*t;1.0,d(n)/dt= -2*n^2*t;0.0,d(t)/dt=1),h)).
:- biocham(list_model).
:- biocham(numerical_simulation(time:10)). % method:msbdf
:- biocham(plot(show:{h})).
:- biocham(plot(show:{h}, logscale:x)).
:- doc('
\\end{example}
').
%! compile_from_pivp(+PIVP_string, +Input , +Output)
%
......@@ -322,13 +366,14 @@ compile_from_pivp(PIVP, Input, Output) :-
type(Input, name), % FF type(Input, expr),
type(Output, name),
doc('
compiles a PIVP into a CRN (with initial concentration values) that computes the value of variable Output at time Input, of the multivariate function f(t) solution of the PIVP.
creates a CRN that implements a function of the variable "Input". The function is specified by the value of the variable "Output" of the solution of a PIVP at time "Input".
The "Input" variable must not appear in the PIVP. It is created and initialized with a parameter named "input".
'),
option(
binomial_reduction,
yesno,
reduction_methods,
_Reduction,
'Determine if the binomial reduction has to be performed'
'Determines if the binomial reduction has to be performed'
),
sort_output(PIVP, Output, PIVP_sorted),
format_pivp(PIVP_sorted, P, Name_list),
......@@ -336,23 +381,11 @@ compile_from_pivp(PIVP, Input, Output) :-
:- doc('
\\begin{example} Compilation of a simple oscillator with 2 species (Lotka-Volterra)\n
').
:- biocham(option(lazy_negatives:yes)).
:- biocham(compile_from_pivp((0.5,(d(x)/dt= x - x*y));(0.25,(d(y)/dt= x*y - 0.5*y)), x)).
:- biocham(list_model).
:- biocham(numerical_simulation(time:10)). % method:msbdf
:- biocham(plot(show:{'x','y'})).
:- doc('
\\end{example}
').
:- doc('
\\begin{example} Compilation of a Hill function of order 2 as a function of input\n
\\begin{example} Compilation of the Hill function of order 2 as a function of an input $h(x)=x^2/(1+x^2)$ \n
').
:- biocham(option(lazy_negatives:yes)).
:- biocham(option(binomial_reduction:yes)).
:- biocham(compile_from_pivp((0.0,d(h)/dt= 2*x*x*y;1.0,d(x)/dt= -2*x*x*y;0.0,d(y)/dt=1),in,h)).
%:- biocham(option(lazy_negatives:yes)).
:- biocham(option(binomial_reduction:native)).
:- biocham(compile_from_pivp((0.0,d(h)/dt= 2*n^2*t;1.0,d(n)/dt= -2*n^2*t;0.0,d(t)/dt=1),x,h)).
:- biocham(parameter(input=2)).
:- biocham(list_model).
:- biocham(numerical_simulation(time:10)). % method:msbdf
......@@ -400,7 +433,7 @@ main_compiler(PIVP_input, Name_list_raw, Input):-
append(Name_list_raw, [Input], Name_list_non_bin)
),
(
Reduction = yes
Reduction \= no
->
reduce_to_binomial(PIVP_non_bin, Name_list_non_bin, PIVP_unsigned, Name_list_unsigned)
;
......@@ -501,8 +534,8 @@ parse_pivp((_,(d(_)/dt=P)), L, [Pol]):-
parse_polynomial(+ X, L, P):-
parse_polynomial(X, L, P).
parse_polynomial(- X, L, [D, R]):-
parse_polynomial(X, L, [C, R]),
parse_polynomial(- X, L, [[D, R]]):-
parse_monomial(X, L, [C, R]),
D is -C.
parse_polynomial(X + Y, L, R):-
......@@ -1235,20 +1268,25 @@ max_list([Head1|Tail1],[Head2|Tail2],[HeadMax|TailMax]) :-
varlist_is_binomial(_Var_list, []) :- !.
varlist_is_binomial(Var_list, [Der|Tail]) :-
once(derivative_is_binomial(Var_list, Der)),
derivative_is_binomial(Var_list, Der),
varlist_is_binomial(Var_list, Tail).
derivative_is_binomial(_Var_list, []) :- !.
derivative_is_binomial(Var_list, [[_Coeff, Expo]|Tail]) :-
(
constant(Expo)
;
member(Expo,Var_list)
;
member(X, Var_list), member(Y, Var_list),
add_list(X, Y, Expo)
),
),!,
derivative_is_binomial(Var_list, Tail).
constant([]) :- !.
constant([0|Tail]) :-
constant(Tail).
%! breadth_search(+PODE, +ListVariable, -Redvar)
%
......@@ -1260,11 +1298,18 @@ breadth_search(PODE, ListVariable, Redvar) :-
construct_all_derivatives(PODE,ListVariable,ListDerivative),
length(Head,L), Lm is L - 1,
displace_exponent([1],0,Lm,First_Var),
Test_list = [[First_Var]],
breadth_search_subroutine(Test_list, ListVariable, ListDerivative, _Next_level, Redvar_t0),
% place First_Var in head
delete(Redvar_t0, First_Var, Redvar_t1),
Redvar = [First_Var|Redvar_t1].
get_option(binomial_reduction, Reduction),
(
Reduction == sat
->
maxsat_binomial_reduction(ListVariable, ListDerivative, First_Var, Redvar)
;
Test_list = [[First_Var]],
breadth_search_subroutine(Test_list, ListVariable, ListDerivative, _Next_level, Redvar_t0),
% place First_Var in head
delete(Redvar_t0, First_Var, Redvar_t1),
Redvar = [First_Var|Redvar_t1]
).
breadth_search_subroutine([], ListVariable, ListDerivative, Next_level, Redvar) :-
breadth_search_subroutine(Next_level, ListVariable, ListDerivative, [], Redvar).
......@@ -1756,3 +1801,189 @@ stutter(N, In, Out) :-
Nm is N-1,
stutter(Nm, p, Tempo),
atom_concat(In, Tempo, Out).
%! maxsat_binomial_reduction(+AllVariables, +AllDerivatives, +OutVar, -Solution) is det.
%
% Calls a MAX-Sat solver to find the smallest binomial reduction of PODE
maxsat_binomial_reduction(AllVariables, AllDerivatives, OutVar, Solution) :-
debug(pivp, "all vars: ~w~nall derivs: ~w", [AllVariables, AllDerivatives]),
% we create a boolean var for each possible ODE var
% except the output one they will all be associated to a soft clause
% hence their number is a good Top value for the hard clauses
length(AllVariables, N),
nth1(OutIndex, AllVariables, OutVar),
derivatives_to_variables(AllDerivatives, AllVariables, Coverings),
% we overestimate the number of clauses, but rc2.py is ok with that
maplist(length, Coverings, NCov),
sum_list(NCov, NClauses),
tmp_file_stream(text, FileRoot, StreamFileInInit),
close(StreamFileInInit),
string_concat(FileRoot, ".wcnf", FileIn),
debug(pivp, "Will be writing clauses to: ~w", [FileIn]),
setup_call_cleanup(
open(FileIn, write, Stream),
(
% header
format(Stream, "p wcnf ~d ~d ~d~n", [N, NClauses, N]),
write_variable_clauses(Stream, N, OutIndex),
write_derivative_covering_clauses(Stream, N, Coverings)
),
close(Stream)
),
call_cleanup(
(
% there is always a solution, so we get solution and weight
run_sat(FileIn, yes, [ResVariables, _Weight]),
variable_string_to_solution(ResVariables, AllVariables, Solution)
),
delete_file(FileIn)
).
%! write_variable_clauses(+Stream, +N, +OutIndex) is det.
%
% Write to stream one clause per possible variable to include.
% it is a hard clause (weight is N) if it is the output variable
% otherwise the negation is a soft clause
write_variable_clauses(Stream, N, OutIndex) :-
forall(
between(1, N, I),
(
(
I == OutIndex
->
Weight = N,
Value = I
;
Weight = 1,
Value is -I
),
format(Stream, "~d ~d 0~n", [Weight, Value])
)
).
%! write_derivative_covering_clauses(Stream, N, Coverings)
%
% Write clause that impose that if a variable is present, its derivative is covered by
% present variables
write_derivative_covering_clauses(Stream, N, Coverings) :-
forall(
between(1, N, I),
(
nth1(I, Coverings, Cov),
II is -I,
forall(
(
member(Co, Cov),
% Optimisation on trivially satisfied clauses
\+ member(I, Co)
),
(
% II is not at the right position in clause, but rc2.py is ok
% with that too
atomic_list_concat([II | Co], ' ', Clause),
format(Stream, "~d ~w 0~n", [N, Clause])
)
)
)
).
%! variable_string_to_solution(+String: string, AllVariables: list, -Solution: list) is det.
%
% Convert the output of the MaxSAT solver (-1 2 3 -4 ...) to a list of the
% variables that are present (i.e. positive)
variable_string_to_solution(String, AllVariables, Solution) :-
split_string(String, " ", " ", VarStrings),
maplist(to_int, VarStrings, VarNumbers),
% keep only the present variables
exclude(>(0), VarNumbers, SolutionIndexes),
findall(
Var,
(
member(Idx, SolutionIndexes),
nth1(Idx, AllVariables, Var)
),
Solution
).
%! derivatives_to_variables(+AllDerivatives, +AllVariables, -Coverings) is det.
%
% Return a list corresponding to the clauses for covering each derivative
derivatives_to_variables(AllDerivatives, AllVariables, Coverings) :-
maplist(derivative_to_variables(AllVariables), AllDerivatives, Coverings).
%! derivative_to_variables(+AllVariables, +Derivative, -Covering) is det.
%
% Covering is a list of clauses covering all monomials of Derivative
derivative_to_variables(AllVariables, Derivative, Covering) :-
maplist(monomial_to_variables(AllVariables), Derivative, Coverings),
% we flatten our conjunction of conjunctions
append(Coverings, Covering).
%! monomial_to_variables(+AllVariables, +Monomial, -Covering) is det.
%
% make a list of clauses for the possible coverings of Monomial (ignoring its constant
% part) by variables of AllVariables
monomial_to_variables(_Variables, [_, Monomial], []) :-
% The derivative is constant, we return the empty conjunction
max_list(Monomial, 0),
!.
monomial_to_variables(AllVariables, [_, Monomial], Covering) :-
% all sums of two different variables
findall(
[I1, I2],
(
nth1(I1, AllVariables, V1),
nth1(I2, AllVariables, V2),
I1 < I2,
add_list(V1, V2, Monomial)
),
Coverings
),
% possible square of a variable
(
nth1(I, AllVariables, V),
add_list(V, V, Monomial)
->
SQCoverings = [[I] | Coverings]
;
SQCoverings = Coverings
),
% or even an existing variable
(
nth1(N, AllVariables, Monomial)
->
DNFCovering = [[N] | SQCoverings]
;
DNFCovering = SQCoverings
),
dnf_cnf(DNFCovering, Covering),
debug(pivp, "Monomial: ~w~nDNF: ~w~nCNF: ~w~n~n", [Monomial, DNFCovering, Covering]).
%! dnf_cnf(+DNF:list, -CNF:list) is det.
%
% simple DNF to CNF conversion
dnf_cnf(DNF, CNF) :-
findall(
L,
(
maplist(member, L0, DNF),
sort(L0, L)
),
CNF
).
%! to_int(+String:atom, -Int:integer) is det.
%
% Read an Int from a String
to_int(String, Int) :-
read_term_from_atom(String, Int, []),
integer(Int).
......@@ -80,6 +80,10 @@ test(add_all_sons) :-
gpac:add_all_sons([a],[a, b, c],[[a, c]],[[a, b]]),
gpac:add_all_sons([a],[a, b, c],[],[[a, b], [a, c]]).
test(derivative_is_binomial) :-
gpac:derivative_is_binomial([[1,0],[0,2]],
[[1,[0,0]],[1,[1,0]],[1,[1,2]],[1,[2,0]]]).
%%% Test of PIVP manipulation %%%
test(compute_derivative1) :-
......
......@@ -52,6 +52,8 @@
:- devdoc('\\section{Grammars}').
:- doc('Different graphs can be created from Biocham models and manipulated and more importantly visualized using the third-party Graphviz visualization tool.').