|
355 | 355 | k = rand(rng, numparams(rn))
|
356 | 356 | rates = Dict(zip(parameters(rn), k))
|
357 | 357 | @test Catalyst.iscomplexbalanced(rn, rates) == true
|
| 358 | + @test Catalyst.isdetailedbalanced(rn, rates) == false |
358 | 359 | end
|
359 | 360 |
|
360 | 361 | ### STRONG LINKAGE CLASS TESTS
|
@@ -498,6 +499,107 @@ let
|
498 | 499 | @test isempty(cyclemat)
|
499 | 500 | end
|
500 | 501 |
|
| 502 | +### Complex and detailed balance tests |
| 503 | + |
| 504 | +# The following network is conditionally complex balanced - it only |
| 505 | + |
| 506 | +# Reversible, forest-like deficiency zero network - should be detailed balance for any choice of rate constants. |
| 507 | +let |
| 508 | + rn = @reaction_network begin |
| 509 | + (k1, k2), A <--> B + C |
| 510 | + (k3, k4), A <--> D |
| 511 | + (k5, k6), A + D <--> E |
| 512 | + (k7, k8), A + D <--> G |
| 513 | + (k9, k10), G <--> 2F |
| 514 | + (k11, k12), A + E <--> H |
| 515 | + end |
| 516 | + |
| 517 | + k1 = rand(rng, numparams(rn)) |
| 518 | + rates1 = Dict(zip(parameters(rn), k1)) |
| 519 | + k2 = rand(StableRNG(232), numparams(rn)) |
| 520 | + rates2 = Dict(zip(parameters(rn), k2)) |
| 521 | + |
| 522 | + rcs, D = reactioncomplexes(rn) |
| 523 | + @test Catalyst.isforestlike(rn) == true |
| 524 | + @test Catalyst.isdetailedbalanced(rn, rates1) == true |
| 525 | + @test Catalyst.isdetailedbalanced(rn, rates2) == true |
| 526 | +end |
| 527 | + |
| 528 | +# Simple connected reversible network |
| 529 | +let |
| 530 | + rn = @reaction_network begin |
| 531 | + (k1, k2), A <--> B |
| 532 | + (k3, k4), B <--> C |
| 533 | + (k5, k6), C <--> A |
| 534 | + end |
| 535 | + |
| 536 | + rcs, D = reactioncomplexes(rn) |
| 537 | + rates1 = [:k1=>1.0, :k2=>1.0, :k3=>1.0, :k4=>1.0, :k5=>1.0, :k6=>1.0] |
| 538 | + @test Catalyst.isdetailedbalanced(rn, rates1) == true |
| 539 | + rates2 = [:k1=>2.0, :k2=>1.0, :k3=>1.0, :k4=>1.0, :k5=>1.0, :k6=>1.0] |
| 540 | + @test Catalyst.isdetailedbalanced(rn, rates2) == false |
| 541 | +end |
| 542 | + |
| 543 | +# Independent cycle tests: the following reaction entwork has 3 out-of-forest reactions. |
| 544 | +let |
| 545 | + rn = @reaction_network begin |
| 546 | + (k1, k2), A <--> B + C |
| 547 | + (k3, k4), A <--> D |
| 548 | + (k5, k6), B + C <--> D |
| 549 | + (k7, k8), A + D <--> E |
| 550 | + (k9, k10), G <--> 2F |
| 551 | + (k11, k12), A + D <--> G |
| 552 | + (k13, k14), G <--> E |
| 553 | + (k15, k16), 2F <--> E |
| 554 | + (k17, k18), A + E <--> H |
| 555 | + end |
| 556 | + |
| 557 | + rcs, D = reactioncomplexes(rn) |
| 558 | + k = rand(rng, numparams(rn)) |
| 559 | + p = parameters(rn) |
| 560 | + rates = Dict(zip(parameters(rn), k)) |
| 561 | + @test Catalyst.isdetailedbalanced(rn, rates) == false |
| 562 | + |
| 563 | + # Adjust rate constants to obey the independent cycle conditions. |
| 564 | + rates[p[6]] = rates[p[1]]*rates[p[4]]*rates[p[5]] / (rates[p[2]]*rates[p[3]]) |
| 565 | + rates[p[14]] = rates[p[13]]*rates[p[11]]*rates[p[8]] / (rates[p[12]]*rates[p[7]]) |
| 566 | + rates[p[16]] = rates[p[8]]*rates[p[15]]*rates[p[9]]*rates[p[11]] / (rates[p[7]]*rates[p[12]]*rates[p[10]]) |
| 567 | + @test Catalyst.isdetailedbalanced(rn, rates) == true |
| 568 | +end |
| 569 | + |
| 570 | +# Deficiency two network: the following reaction network must satisfy both the independent cycle conditions and the spanning forest conditions |
| 571 | +let |
| 572 | + rn = @reaction_network begin |
| 573 | + (k1, k2), 3A <--> A + 2B |
| 574 | + (k3, k4), A + 2B <--> 3B |
| 575 | + (k5, k6), 3B <--> 2A + B |
| 576 | + (k7, k8), 2A + B <--> 3A |
| 577 | + (k9, k10), 3A <--> 3B |
| 578 | + end |
| 579 | + |
| 580 | + rcs, D = reactioncomplexes(rn) |
| 581 | + @test Catalyst.edgeindex(D, 1, 2) == 1 |
| 582 | + @test Catalyst.edgeindex(D, 4, 3) == 6 |
| 583 | + k = rand(rng, numparams(rn)) |
| 584 | + p = parameters(rn) |
| 585 | + rates = Dict(zip(parameters(rn), k)) |
| 586 | + @test Catalyst.isdetailedbalanced(rn, rates) == false |
| 587 | + |
| 588 | + # Adjust rate constants to fulfill independent cycle conditions. |
| 589 | + rates[p[8]] = rates[p[7]]*rates[p[5]]*rates[p[9]] / (rates[p[6]]*rates[p[10]]) |
| 590 | + rates[p[3]] = rates[p[2]]*rates[p[4]]*rates[p[9]] / (rates[p[1]]*rates[p[10]]) |
| 591 | + @test Catalyst.isdetailedbalanced(rn, rates) == false |
| 592 | + # Should still fail - doesn't satisfy spanning forest conditions. |
| 593 | + |
| 594 | + # Adjust rate constants to fulfill spanning forest conditions. |
| 595 | + cons = rates[p[6]] / rates[p[5]] |
| 596 | + rates[p[1]] = rates[p[2]] * cons |
| 597 | + rates[p[9]] = rates[p[10]] * cons^(3/2) |
| 598 | + rates[p[8]] = rates[p[7]]*rates[p[5]]*rates[p[9]] / (rates[p[6]]*rates[p[10]]) |
| 599 | + rates[p[3]] = rates[p[2]]*rates[p[4]]*rates[p[9]] / (rates[p[1]]*rates[p[10]]) |
| 600 | + @test Catalyst.isdetailedbalanced(rn, rates) == true |
| 601 | +end |
| 602 | + |
501 | 603 | ### Other Network Properties Tests ###
|
502 | 604 |
|
503 | 605 | # Tests outgoing complexes matrices (1).
|
@@ -637,6 +739,108 @@ let
|
637 | 739 | @test Catalyst.robustspecies(EnvZ_OmpR) == [6]
|
638 | 740 | end
|
639 | 741 |
|
| 742 | + |
| 743 | +### Complex and detailed balance tests |
| 744 | + |
| 745 | +# The following network is conditionally complex balanced - it only |
| 746 | + |
| 747 | +# Reversible, forest-like deficiency zero network - should be detailed balance for any choice of rate constants. |
| 748 | +let |
| 749 | + rn = @reaction_network begin |
| 750 | + (k1, k2), A <--> B + C |
| 751 | + (k3, k4), A <--> D |
| 752 | + (k5, k6), A + D <--> E |
| 753 | + (k7, k8), A + D <--> G |
| 754 | + (k9, k10), G <--> 2F |
| 755 | + (k11, k12), A + E <--> H |
| 756 | + end |
| 757 | + |
| 758 | + k1 = rand(rng, numparams(rn)) |
| 759 | + rates1 = Dict(zip(parameters(rn), k1)) |
| 760 | + k2 = rand(StableRNG(232), numparams(rn)) |
| 761 | + rates2 = Dict(zip(parameters(rn), k2)) |
| 762 | + |
| 763 | + rcs, D = reactioncomplexes(rn) |
| 764 | + @test Catalyst.isforestlike(rn) == true |
| 765 | + @test Catalyst.isdetailedbalanced(rn, rates1) == true |
| 766 | + @test Catalyst.isdetailedbalanced(rn, rates2) == true |
| 767 | +end |
| 768 | + |
| 769 | +# Simple connected reversible network |
| 770 | +let |
| 771 | + rn = @reaction_network begin |
| 772 | + (k1, k2), A <--> B |
| 773 | + (k3, k4), B <--> C |
| 774 | + (k5, k6), C <--> A |
| 775 | + end |
| 776 | + |
| 777 | + rcs, D = reactioncomplexes(rn) |
| 778 | + rates1 = [:k1=>1.0, :k2=>1.0, :k3=>1.0, :k4=>1.0, :k5=>1.0, :k6=>1.0] |
| 779 | + @test Catalyst.isdetailedbalanced(rn, rates1) == true |
| 780 | + rates2 = [:k1=>2.0, :k2=>1.0, :k3=>1.0, :k4=>1.0, :k5=>1.0, :k6=>1.0] |
| 781 | + @test Catalyst.isdetailedbalanced(rn, rates2) == false |
| 782 | +end |
| 783 | + |
| 784 | +# Independent cycle tests: the following reaction entwork has 3 out-of-forest reactions. |
| 785 | +let |
| 786 | + rn = @reaction_network begin |
| 787 | + (k1, k2), A <--> B + C |
| 788 | + (k3, k4), A <--> D |
| 789 | + (k5, k6), B + C <--> D |
| 790 | + (k7, k8), A + D <--> E |
| 791 | + (k9, k10), G <--> 2F |
| 792 | + (k11, k12), A + D <--> G |
| 793 | + (k13, k14), G <--> E |
| 794 | + (k15, k16), 2F <--> E |
| 795 | + (k17, k18), A + E <--> H |
| 796 | + end |
| 797 | + |
| 798 | + rcs, D = reactioncomplexes(rn) |
| 799 | + k = rand(rng, numparams(rn)) |
| 800 | + p = parameters(rn) |
| 801 | + rates = Dict(zip(parameters(rn), k)) |
| 802 | + @test Catalyst.isdetailedbalanced(rn, rates) == false |
| 803 | + |
| 804 | + # Adjust rate constants to obey the independent cycle conditions. |
| 805 | + rates[p[6]] = rates[p[1]]*rates[p[4]]*rates[p[5]] / (rates[p[2]]*rates[p[3]]) |
| 806 | + rates[p[14]] = rates[p[13]]*rates[p[11]]*rates[p[8]] / (rates[p[12]]*rates[p[7]]) |
| 807 | + rates[p[16]] = rates[p[8]]*rates[p[15]]*rates[p[9]]*rates[p[11]] / (rates[p[7]]*rates[p[12]]*rates[p[10]]) |
| 808 | + @test Catalyst.isdetailedbalanced(rn, rates) == true |
| 809 | +end |
| 810 | + |
| 811 | +# Deficiency two network: the following reaction network must satisfy both the independent cycle conditions and the spanning forest conditions |
| 812 | +let |
| 813 | + rn = @reaction_network begin |
| 814 | + (k1, k2), 3A <--> A + 2B |
| 815 | + (k3, k4), A + 2B <--> 3B |
| 816 | + (k5, k6), 3B <--> 2A + B |
| 817 | + (k7, k8), 2A + B <--> 3A |
| 818 | + (k9, k10), 3A <--> 3B |
| 819 | + end |
| 820 | + |
| 821 | + rcs, D = reactioncomplexes(rn) |
| 822 | + @test Catalyst.edgeindex(D, 1, 2) == 1 |
| 823 | + @test Catalyst.edgeindex(D, 4, 3) == 6 |
| 824 | + k = rand(rng, numparams(rn)) |
| 825 | + p = parameters(rn) |
| 826 | + rates = Dict(zip(parameters(rn), k)) |
| 827 | + @test Catalyst.isdetailedbalanced(rn, rates) == false |
| 828 | + |
| 829 | + # Adjust rate constants to fulfill independent cycle conditions. |
| 830 | + rates[p[8]] = rates[p[7]]*rates[p[5]]*rates[p[9]] / (rates[p[6]]*rates[p[10]]) |
| 831 | + rates[p[3]] = rates[p[2]]*rates[p[4]]*rates[p[9]] / (rates[p[1]]*rates[p[10]]) |
| 832 | + @test Catalyst.isdetailedbalanced(rn, rates) == false |
| 833 | + # Should still fail - doesn't satisfy spanning forest conditions. |
| 834 | + |
| 835 | + # Adjust rate constants to fulfill spanning forest conditions. |
| 836 | + cons = rates[p[6]] / rates[p[5]] |
| 837 | + rates[p[1]] = rates[p[2]] * cons |
| 838 | + rates[p[9]] = rates[p[10]] * cons^(3/2) |
| 839 | + rates[p[8]] = rates[p[7]]*rates[p[5]]*rates[p[9]] / (rates[p[6]]*rates[p[10]]) |
| 840 | + rates[p[3]] = rates[p[2]]*rates[p[4]]*rates[p[9]] / (rates[p[1]]*rates[p[10]]) |
| 841 | + @test Catalyst.isdetailedbalanced(rn, rates) == true |
| 842 | +end |
| 843 | + |
640 | 844 | ### DEFICIENCY ONE TESTS
|
641 | 845 |
|
642 | 846 | # Fails because there are two terminal linkage classes in the linkage class
|
|
0 commit comments