Commit 8336d17e authored by SOLIMAN Sylvain's avatar SOLIMAN Sylvain
Browse files

Merge branch 'release/4.4.14'

parents d0e39c30 fc477c81
......@@ -199,7 +199,7 @@ gadagne_restart: gadagne
ssh -t gadagne 'cd /opt/tmpnb/ && sudo ./cleanup.sh && sudo ./tmpnb_biocham.sh'
release:
echo "current version: " && grep '^version' about.pl
@echo "current version: " && grep '^version' about.pl
@read -p "Version number (e.g. 4.1.0): " version && \
git flow release start "$$version" && \
sed -i"" -e "s/^version('.*')/version('$$version')/" about.pl && \
......
......@@ -8,7 +8,7 @@
about/0
]).
version('4.4.13').
version('4.4.14').
copyright(
'Copyright (C) 2003-2020 Inria, EPI Lifeware, Saclay-Île de France, France'
......
......@@ -389,7 +389,7 @@ insert_coef(A, Coef, Result) :-
Coef > 0,
Coef < 1,
CoefInv is 1 / Coef,
0.0 is float_fractional_part(CoefInv)
0.0 =:= float_fractional_part(CoefInv)
->
normalize_number(CoefInv, CoefInvNorm),
Result = A / CoefInvNorm
......
FROM registry.gitlab.inria.fr/lifeware/biocham:v4.4.13
FROM registry.gitlab.inria.fr/lifeware/biocham:v4.4.14
This diff is collapsed.
list_reduction:- use_module(library(plunit)).
:- use_module(binomial_reduction).
:- begin_tests(binomial_reduction, [setup((clear_model, reset_options))]).
test(polynomial_ODE_True) :-
command(a => b),
command(3*a^2 for a+a => c),
with_output_to(atom(_), reduction:polynomial_ODE).
test(polynomial_ODE_False) :-
command(a/b for a => b),
with_output_to(atom(_), \+ reduction:polynomial_ODE).
test(generate_names) :-
reduction:generate_names([a,'Out'], [[1,2],[1,0]], [aOut2, a]).
test(compute_number_species) :-
reduction:compute_number_species([3,1],8,0),
reduction:compute_number_species([0,3,0,1,0],8,3),
reduction:compute_number_species([2,2,0],9,1).
test(generate_sufficient_variables1) :-
PODE = [[[1,[1, 1]],[3,[0, 1]]],[[-1,[3, 0]]]],
reduction:generate_sufficient_variables(PODE,Set),
once(permutation(Set, [[1,0],[0,1],[2,0],[1,1],[3,0],[2,1]])).
test(generate_sufficient_variables2) :-
PODE = [[[1,[3, 1]]],[[-1,[1, 3]]]],
reduction:generate_sufficient_variables(PODE,Set),
once(permutation(Set, [[1,0],[0,1],[2,0],[1,1],[0,2],[3,0],[1,2],[2,1],[0,3],
[3,1],[2,2],[1,3],[3,2],[2,3]])).
test(generate_sufficient_variables3) :-
PODE = [[[1,[0, 3]]],[[-1,[0, 2]]]],
reduction:generate_sufficient_variables(PODE,Set),
once(permutation(Set, [[1,0],[0,1],[0,2]])).
test(list_reduction_nomodif) :-
once(reduction:list_reduction([2,[[[1,[1, 1]]],[[-1,[1, 0]]]],[1,2]],[a,b],P,[a,b])),
P = [2,[[[1,[1, 1]]],[[-1,[1, 0]]]],[1,2]].
test(list_reduction_simple) :-
once(reduction:list_reduction([2,[[[1,[1, 2]]],[[-1,[1, 0]]]],[1,2]],[a,b],P,[a,b,b2])),
P = [3,[[[1, [1, 0, 1]]], [[-1, [1, 0, 0]]], [[-2, [1, 1, 0]]]],[1,2,4]].
test(scan_order_multivar) :-
reduction:scan_order_multivar([[1,[1, 0]],[1,[1, 1]]],2),
reduction:scan_order_multivar([[1,[1, 2]],[1,[1, 1]]],3),
reduction:scan_order_multivar([[1,[1, 2]],[1,[12, 1]]],13).
test(add_all_sons) :-
reduction:add_all_sons([a, b],[a, b, c],[],[[a, b, c]]),
reduction:add_all_sons([a],[a, b, c],[[a, c]],[[a, b]]),
reduction:add_all_sons([a],[a, b, c],[],[[a, b], [a, c]]).
test(derivative_is_binomial) :-
reduction: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) :-
PODE = [ [[1,[0,1]],[2,[1,1]]] , [[3,[1,0]]] ],
reduction:compute_derivative([1,1], PODE, Deriv),
once(permutation(Deriv, [[3,[2,0]],[1,[0,2]],[2,[1,2]]])).
test(compute_derivative2) :-
PODE = [ [[1,[0,1,0]]] , [[3,[1,0,0]],[2,[1,1,0]]] , [[-2,[1,1,0]]] ],
reduction:compute_derivative([0,1,1], PODE, Deriv),
once(permutation(Deriv, [[3,[1,0,1]],[2,[1,1,1]],[-2,[1,2,0]]])).
test(compute_derivative3) :-
PODE = [ [[1,[0,1,0]]] , [[3,[0,2,0]]] , [[-2,[0,1,1]]] ],
reduction:compute_derivative([0,1,1], PODE, Deriv),
once(permutation(Deriv, [[1,[0,2,1]]])).
test(clean_writing) :-
reduction:clean_writing(1.0*2, 2),
reduction:clean_writing(0.0*2, 0),
reduction:clean_writing(1.0*input, input),
reduction:clean_writing(input^1, input),
reduction:clean_writing(input^2, input^2).
test(convert_poly_to_ode) :-
reduction:convert_poly_to_ode([[1, [1,1]]], [a,b], a*b),
reduction:convert_poly_to_ode([[1, [1,1]], [-2, [0,2]]], [a,b], -(2*b^2) + a*b).
:- end_tests(binomial_reduction).
{
"name": "gui",
"version": "4.4.13",
"version": "4.4.14",
"description": "biocham gui in jupyter notebook",
"main": "src/index.js",
"scripts": {
......
......@@ -24,6 +24,7 @@ commands = [
"add_vertex",
"alias",
"bifurcations",
"binomial_reduction_ODE",
"canonical",
"change_parameter_to_variable",
"check_conservations",
......@@ -146,6 +147,7 @@ commands = [
"list_stable_states",
"list_tables",
"list_tscc_candidates",
"list_units",
"load",
"load_biocham",
"load_ginml",
......@@ -179,6 +181,7 @@ commands = [
"pattern_reduction",
"place",
"plot",
"polynomial_ODE",
"present",
"prolog",
"quit",
......@@ -186,7 +189,9 @@ commands = [
"rate_independence_reduction_inputs_sinks",
"reaction_graph",
"reaction_model",
"read_xpp",
"reduce_model",
"remove_fraction",
"rename_column",
"rename_table",
"reset_options",
......@@ -211,6 +216,7 @@ commands = [
"set_model_name",
"set_ode_system_name",
"set_p_m_rate",
"set_units",
"test_rate_independence",
"test_rate_independence_inputs_sinks",
"test_rate_independence_invariants",
......@@ -220,4 +226,6 @@ commands = [
"validity_domain",
"variation",
"which_p_m_mode",
"with_timer",
"write_xpp",
]
"""Example magic"""
__version__ = '4.4.13'
__version__ = '4.4.14'
......@@ -69,6 +69,7 @@ def get_data_and_meta(filename):
to_show = metadata[1].split(' ')[1:]
against = metadata[2].split(' ')[1]
axes = metadata[3].split(' ')[1:5]
axes_names = (' '.join(metadata[4].split(' ')[1:]), ' '.join(metadata[5].split(' ')[1:]))
with open(filename, newline='') as csvfile:
lines = list(csv.reader(csvfile))
......@@ -78,7 +79,8 @@ def get_data_and_meta(filename):
'logscale': logscale,
'to_show': to_show,
'against': against,
'axes': axes}
'axes': axes,
'axes_names': axes_names}
def create_bokeh_plot(data, gui=False):
......@@ -100,7 +102,8 @@ def create_bokeh_plot(data, gui=False):
bokeh_loaded = True
fig = figure(
x_axis_label=against,
x_axis_label=data['axes_names'][0],
y_axis_label=data['axes_names'][1],
x_axis_type='log' if 'x' in logscale else 'linear',
y_axis_type='log' if 'y' in logscale else 'linear',
tools='pan,wheel_zoom,box_zoom,reset,save,undo',
......
......@@ -35,7 +35,7 @@ The following files can also be used to export some Biocham objects:
:- doc('\\section{Biocham call options}').
do_arguments :-
current_prolog_flag(argv, [_Exec | Args]),
current_prolog_flag(os_argv, [_Exec | Args]),
do_arguments(Args).
......
This diff is collapsed.
......@@ -25,82 +25,19 @@ test(format_pivp_higher_order) :-
test(sort_output) :-
gpac:sort_output((1,d(cos)/dt=(-1*sin);0,d(sin)/dt=cos),cos,(1,d(cos)/dt=(-1*sin);0,d(sin)/dt=cos)),
gpac:sort_output((0,d(sin)/dt=cos;1,d(cos)/dt=(-1*sin)),cos,(1,d(cos)/dt=(-1*sin);0,d(sin)/dt=cos)).
gpac:sort_output((0,d(sin)/dt=cos;1,d(cos)/dt=(-1*sin)),cos,(1,d(cos)/dt=(-1*sin);0,d(sin)/dt=cos)),
gpac:sort_output((1.0, (d(f)/dt = f^3));(1.0, (d(g)/dt = f^3));(1.0, (d(h)/dt = g^3)), h,
(1.0, d(h)/dt = g^3);(1.0, d(f)/dt = f^3);(1.0, d(g)/dt = f^3)).
test(extract_names) :-
gpac:extract_names((1, d(a)/dt = 1; 2, d(b)/dt = 2), [a,b]).
test(generate_names) :-
gpac:generate_names([a,'Out'], [[1,2],[1,0]], [aOut2, a]).
%%% Test of g_to_c %%%
test(g_to_c_PIVP) :-
Old_PIVP = [1, [[[-1,[1]]]], [1]],
New_PIVP = [2, [[[-1,[1,1]]],[[-1,[0,1]]]], [1,input]],
gpac:g_to_c_PIVP(Old_PIVP,New_PIVP, 1.0).
%%% Test of binomial reduction %%%
test(compute_number_species) :-
gpac:compute_number_species([3,1],8,0),
gpac:compute_number_species([0,3,0,1,0],8,3),
gpac:compute_number_species([2,2,0],9,1).
test(generate_sufficient_variables1) :-
PODE = [[[1,[1, 1]],[3,[0, 1]]],[[-1,[3, 0]]]],
gpac:generate_sufficient_variables(PODE,Set),
once(permutation(Set, [[1,0],[0,1],[2,0],[1,1],[3,0],[2,1]])).
test(generate_sufficient_variables2) :-
PODE = [[[1,[3, 1]]],[[-1,[1, 3]]]],
gpac:generate_sufficient_variables(PODE,Set),
once(permutation(Set, [[1,0],[0,1],[2,0],[1,1],[0,2],[3,0],[1,2],[2,1],[0,3],
[3,1],[2,2],[1,3],[3,2],[2,3]])).
test(generate_sufficient_variables3) :-
PODE = [[[1,[0, 3]]],[[-1,[0, 2]]]],
gpac:generate_sufficient_variables(PODE,Set),
once(permutation(Set, [[1,0],[0,1],[0,2]])).
test(reduce_to_binomial_nomodif) :-
once(gpac:reduce_to_binomial([2,[[[1,[1, 1]]],[[-1,[1, 0]]]],[1,2]],[a,b],P,[a,b])),
P = [2,[[[1,[1, 1]]],[[-1,[1, 0]]]],[1,2]].
test(reduce_to_binomial_simple) :-
once(gpac:reduce_to_binomial([2,[[[1,[1, 2]]],[[-1,[1, 0]]]],[1,2]],[a,b],P,[a,b,b2])),
P = [3,[[[1, [1, 0, 1]]], [[-1, [1, 0, 0]]], [[-2, [1, 1, 0]]]],[1,2,4]].
test(scan_order_multivar) :-
gpac:scan_order_multivar([[1,[1, 0]],[1,[1, 1]]],2),
gpac:scan_order_multivar([[1,[1, 2]],[1,[1, 1]]],3),
gpac:scan_order_multivar([[1,[1, 2]],[1,[12, 1]]],13).
test(add_all_sons) :-
gpac:add_all_sons([a, b],[a, b, c],[],[[a, b, c]]),
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) :-
PODE = [ [[1,[0,1]],[2,[1,1]]] , [[3,[1,0]]] ],
gpac:compute_derivative([1,1], PODE, Deriv),
once(permutation(Deriv, [[3,[2,0]],[1,[0,2]],[2,[1,2]]])).
test(compute_derivative2) :-
PODE = [ [[1,[0,1,0]]] , [[3,[1,0,0]],[2,[1,1,0]]] , [[-2,[1,1,0]]] ],
gpac:compute_derivative([0,1,1], PODE, Deriv),
once(permutation(Deriv, [[3,[1,0,1]],[2,[1,1,1]],[-2,[1,2,0]]])).
test(compute_derivative3) :-
PODE = [ [[1,[0,1,0]]] , [[3,[0,2,0]]] , [[-2,[0,1,1]]] ],
gpac:compute_derivative([0,1,1], PODE, Deriv),
once(permutation(Deriv, [[1,[0,2,1]]])).
test(invert_PIVP) :-
gpac:exp_PIVP(Exp),
ResPIVP = [2,[[[-1,[2,1]]],[[1,[0,1]]]],[1,1]],
......@@ -118,11 +55,4 @@ test(multiply_PIVP) :-
gpac:multiply_PIVP(PIVP1, PIVP2, PIVP12),
PIVP12 = [3,[[[1,[0,1,1]],[-1,[0,1,2]]],[[1,[0,1,0]]],[[-1,[0,0,2]]]],[1,1,1]].
test(clean_writing) :-
gpac:clean_writing(1.0*2, 2),
gpac:clean_writing(0.0*2, 0),
gpac:clean_writing(1.0*input, input),
gpac:clean_writing(input^1, input),
gpac:clean_writing(input^2, input^2).
:- end_tests(gpac).
......@@ -75,6 +75,8 @@ find_invar_aux(ForcedMax, Type, OtherType, Operator) :-
Goal2 =.. [Type, P],
Goal2,
\+(member(P, Vars)),
% can be added by normalized_path
\+(base_mol([P])),
assertz(base_mol([P]))
),
[P]
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -136,7 +136,7 @@ load(InputFile) :-
type(InputFile, input_file),
doc('
acts as the corresponding \\command{load_biocham/1} / \\command{load_sbml/1}
/ \\command{load_ode/1} / \\command{load_table/1},
/ \\command{import_ode/1} / \\command{load_table/1},
depending on the file extension
(respectively \\texttt{.bc}, \\texttt{.xml}, \\texttt{.ode}, \\texttt{.csv}
-- assuming no extension is \\texttt{.bc}).'),
......
......@@ -86,7 +86,7 @@ normalized([], []) :- !.
normalized(A, Anorm) :-
leading(A, Lc),
mult(float(1 rdiv Lc), A, Anorm).
mult(float(1 / Lc), A, Anorm).
% subtracts two polynomials
subtract(A, B, C) :-
......@@ -123,7 +123,7 @@ long_division(A, B, Q, R) :-
leading(A, LcA),
leading(B, LcB),
D is DegA - DegB,
C is float(LcA rdiv LcB),
C is float(LcA / LcB),
monomial_mult(D, C, B, B1),
subtract(A, B1, A1),
long_division(A1, B, Q1, R), !,
......@@ -163,7 +163,7 @@ simplify_fraction(A, B, Aprime, Bprime) :-
normalized_euclid(A, B, Unorm, Vnorm, Dnorm) :-
euclid(A, B, U, V, D),
leading(D, LcD),
C is float(1 rdiv LcD),
C is float(1 / LcD),
mult(C, U, Unorm),
mult(C, V, Vnorm),
mult(C, D, Dnorm), !.
......@@ -271,7 +271,7 @@ partial_fraction(P, Q, A, QL, ML) :-
% the following three lines ensure Q is unitary
% without altering the fraction P/Q
leading(Q, Lead),
PCoef is float(1 rdiv Lead),
PCoef is float(1 / Lead),
mult(PCoef, P, P_normal),
roots(Q, QLraw, ML),
......
......@@ -133,6 +133,7 @@ filter(only_extrema).
:- initial(option(filter: no_filter)).
:- initial(option(stats: no)).
:- dynamic(last_method/1).
:- devdoc('\\section{Commands}').
......@@ -179,6 +180,9 @@ numerical_simulation :-
'filtering function for the trace'
),
doc('performs a numerical simulation from time 0 up to a given time.'),
retractall(last_method(_)),
debug(numsim, "numerical simulation method: ~w", [Method]),
assertz(last_method(Method)),
statistics(runtime,_),
(
Method = ssa
......@@ -203,7 +207,7 @@ numerical_simulation :-
;
Method = rsbk
->
assertz(rosenbrock_running),
assertz(rosenbrock_running),
with_current_ode_system(
with_clean(
[
......@@ -218,7 +222,6 @@ numerical_simulation :-
solve2
)
),
assertz(last_method_rsbk),
retractall(rosenbrock_running),
retractall(rosenbrock_running2)
;
......@@ -233,8 +236,7 @@ numerical_simulation :-
],
solve
)
),
retractall(last_method_rsbk)
)
),
statistics(runtime,[_,T]),
(
......@@ -258,7 +260,8 @@ continue :-
get_initial_values(Variables, InitialValues),
set_initial_values(Variables, Values),
% FIXME uses global options, not the ones used for previous simulation
( last_method_rsbk
(
last_method(rsbk)
->
nb_setval(rsbk_continue,yes),
with_option([time: Time,method: rsbk], numerical_simulation),
......@@ -305,6 +308,7 @@ prepare_numerical_simulation_options(Options) :-
enumerate_variables,
convert_ode,
gather_equations(Equations),
debug(numsim, "ODEs: ~w", [Equations]),
(
rosenbrock_running
->
......@@ -319,12 +323,16 @@ prepare_numerical_simulation_options(Options) :-
true
),
gather_initial_values(InitialValues),
debug(numsim, "Initial values: ~w", [InitialValues]),
gather_events(RegularEvents, event),
gather_events(TimeEvents, time_event),
append(RegularEvents, TimeEvents, Events),
debug(numsim, "Events: ~w", [Events]),
gather_fields(Events, Fields),
gather_initial_parameter_values(InitialParameterValues),
debug(numsim, "Parameter values: ~w", [InitialParameterValues]),
jacobian(Equations, Jacobian),
debug(numsim, "Jacobian: ~w", [Jacobian]),
Options = [
fields: Fields,
equations: Equations,
......@@ -371,7 +379,9 @@ solve2 :-
solve :-
prepare_numerical_simulation_options(Options),
debug(numsim, "simulation options: ~w", [Options]),
solve(Options, Table),
debug(numsim, "Done", []),
add_table('numerical_simulation', Table).
......@@ -408,6 +418,7 @@ enumerate_nonconstant_parameters(Events, Header: Parameter) :-
),
NCParameters
),
debug(numsim, "NC parameters ~w", [NCParameters]),
member(ParameterIndex, NCParameters),
Parameter = p(ParameterIndex),
convert_identifier(Header, Parameter).
......@@ -417,7 +428,9 @@ enumerate_nonparametric_functions(Header: expression(Expr)) :-
% (item([kind: function, item: function(Header = Body)]) ;
get_ode_function(Header, Body),
atomic(Header),
debug(numsim, "NP function ~w with body ~w", [Header, Body]),
ode:substitute_functions(Body, NoFuncBody),
debug(numsim, "substituted body ~w", [NoFuncBody]),
convert_expression(NoFuncBody, Expr).
......@@ -473,11 +486,13 @@ gather_initial_values(InitialValues) :-
gather_initial_parameter_values(InitialParameterValues) :-
peek_count(parameter_counter, ParameterCount),
ParameterMax is ParameterCount - 1,
debug(numsim, "~d parameters", [ParameterCount]),
findall(
InitialParameterValue,
(
between(0, ParameterMax, ParameterIndex),
parameter_index(Parameter, ParameterIndex),
debug(numsim, "Parameter #~d: ~w", [ParameterIndex, Parameter]),
(%parameter_value(Parameter, InitialParameterValue)
ode_parameter_value(Parameter, InitialParameterValue)
; conditional_parameter_value(ParameterIndex,InitialParameterValue) % virtual event parameters for conditional kinetics
......
......@@ -22,6 +22,7 @@
add_reactions_from_ode_system/0,
add_influences_from_ode_system/0,
(init)/1,
remove_fraction/0,
% load_ode_system/1,
load_reactions_from_ode/1,
% add_ode_system/1,
......@@ -52,7 +53,9 @@
ode/2,
ode/3,
ode_add_expression_to_molecule/2,
ode_predicate/1
ode_predicate/1,
remove_fraction/2,
normalize_ode/1
]).
% Only for separate compilation/linking
......@@ -124,7 +127,10 @@ list_ode :-
with_current_ode_system(
(
get_current_ode_system(Id),
list_items([parent: Id, kind: ode])
list_items([parent: Id, kind: ode]),
list_items([parent: Id, kind: initial_concentration]),
list_items([parent: Id, kind: parameter]),
list_items([parent: Id, kind: function])
)
)
;
......@@ -414,7 +420,31 @@ load_influences_from_ode_system :-
add_influences_from_ode_system.
remove_fraction :-
biocham_command,
doc('Remove the rational fraction in the current ODE system by multiplying the derivative
by the GCD. WARNING: the resulting ODE system is NOT equivalent to the starting one.'),
new_ode_system(NewId),
set_ode_system_name(NewId, without_fraction_ode),
normalize_ode(ListOde),
maplist(remove_fraction, ListOde, NewListOde),
maplist(add_ode(NewId), NewListOde),
with_current_ode_system((
get_current_ode_system(Id),
forall(
item([parent: Id, kind:parameter, item: Item]),
add_item([parent: NewId, kind:parameter, item: Item])
),
forall(
item([parent: Id, kind: function, item: Item]),
add_item([parent: NewId, kind: function, item: Item])
),
forall(
item([parent: Id, kind:initial_concentration, item: Item]),
add_item([parent: NewId, kind:initial_concentration, item: Item])
)
)),
select_ode_system(without_fraction_ode).
:- devdoc('\\section{Public API}').
......@@ -559,6 +589,18 @@ add_ode(Id, ODE) :-
add_item([parent: Id, kind: ode, key: X, item: Item]).
change_ode(Id, ODE) :-
(
parse_ode(ODE, X, Item)
->
true
;
throw(error(illformed_ode(ODE)))
),
delete_item([parent: Id, kind: ode, key: X]),
add_item([parent: Id, kind: ode, key: X, item: Item]).
import_reactions_from_ode_system(Id) :-
check_cleaned(ode:assoc/2),
enumerate_terms_in_odes(Id),
......@@ -662,25 +704,10 @@ non_decomposable_term_in_expression(Coefficient, Term, E) :-
substitute_functions(E, ENoFunc) :-
(
get_current_ode_system(Id)
->
all_items([parent: Id, kind: function], Functions),
maplist(split_func, Functions, Definitions, Bodies),
maplist(
rewrite(substitute(Definitions, Bodies)),
Bodies,
NoFuncBods
),
substitute(Definitions, NoFuncBods, E, ENoFunc)
;
% case of add_function, there is no current system, but no functions
% either
ENoFunc = E
).
kinetics:eval_kinetics([], [], E, ENoFunc), % with kinetics/4 get some errors on simplification
!.
split_func(func(F = D), F, D).
substitute_functions(E, E). % case of add_function, there is no current system, but no functions
non_decomposable_term_in_additive_normal_form(Coefficient, Term, A + B) :-
......@@ -1259,3 +1286,185 @@ ode_add_expression_to_molecule(NewExpression, Molecule) :-
;
asserta(assoc(Molecule, NewExpression))
).
%! remove_fraction(+Expr, +ListVar, -NewExpr)
%
% remove all the fraction in an expression by multiplying all terms by the common
% denominator
remove_fraction(d( X )/dt = Expr, d( X )/dt = NewExpr) :-
find_common_denominator(Expr, Denominator),
multiply_each_term(Expr, Denominator, ExprTempo),
simplify(ExprTempo, NewExpr).
%! find_common_denominator(+Expr, +ListVariable, -ListDenominator)
%
% list all the denominator present in Expr, ListVariable is given as context in order to
% be able to skip parameters
find_common_denominator(Expr + _Num/Den, [Den|Tail]) :-