GithubHelp home page GithubHelp logo

kenmcmil / ivy Goto Github PK

View Code? Open in Web Editor NEW

This project forked from microsoft/ivy

75.0 75.0 23.0 59.4 MB

IVy is a research tool intended to allow interactive development of protocols and their proofs of correctness and to provide a platform for developing and experimenting with automated proof techniques. In particular, IVy provides interactive visualization of automated proofs, and supports a use model in which the human protocol designer and the automated tool interact to expose errors and prove correctness.

License: Other

Python 30.17% JavaScript 0.44% Jupyter Notebook 0.87% Shell 0.10% HTML 0.03% Emacs Lisp 0.07% Makefile 0.01% C# 2.23% C++ 66.08%

ivy's People

Contributors

aaptel avatar calvinclaus avatar egorbu avatar fmrl avatar hamidralmasi avatar hannesm avatar kant avatar kenmcmil avatar marcelotaube avatar mister-walter avatar mlr-msft avatar msftgits avatar odedp avatar shubhambhattar avatar stutibiyani avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

ivy's Issues

HavocAction does not handle destructor assignment

Here's an example:

#lang ivy1.7

type obj = struct {
    s : bool
}

individual my_obj: obj

after init {
    my_obj.s := false;
}

export action act = {
    var o:obj;
    o.s := *;
}

invariant my_obj.s = false

The invariant breaks when it should not.

Code gerneration: deterministic "non-deterministic" choice

Following http://microsoft.github.io/ivy/language.html#non-deterministic-choice I found a bug with the code generation of non-deterministic choices. Example ivy program:

#lang ivy1.7

type money
object account = {
    individual balance : money

    after init {
        balance := 0
    }

    action rand_add returns (x:money) = {
        if * {
            x := 1
        } else {
            x := 2
        }
        balance := balance + x;
        # ensure x = 1 | x = 2
    }

    action get_balance returns(x:money) = {
        x := balance
    }
}

export account.rand_add
export account.get_balance

interpret money -> bv[16]

Expected behavior: rand_add randomly adds 1 or 2 to the balance.
Actual behaviour: generated code of rand_add only ever adds 1 to the balance.

Output of the test (same behavior in the repl)
$ ivy_to_cpp target=test build=true correct_rand.ivy 
g++ -Wno-parentheses-equality  -std=c++11  -I /usr/lib/python2.7/site-packages/ivy/include -L /usr/lib/python2.7/site-packages/ivy/lib -Xlinker -rpath -Xlinker /usr/lib/python2.7/site-packages/ivy/lib -g -o correct_rand correct_rand.cpp -lz3 -pthread
$ ./correct_rand 
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 2
> account.get_balance
= 2
> account.rand_add
= 1
> account.get_balance
= 3
> account.rand_add
= 1
> account.get_balance
= 4
> account.get_balance
= 4
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 6
> account.get_balance
= 6
> account.rand_add
= 1
> account.get_balance
= 7
> account.get_balance
= 7
> account.get_balance
= 7
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 9
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 19
> account.get_balance
= 19
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 22
> account.rand_add
= 1
> account.get_balance
= 23
> account.get_balance
= 23
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 25
> account.get_balance
= 25
> account.get_balance
= 25
> account.get_balance
= 25
> account.rand_add
= 1
> account.get_balance
= 26
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 28
> account.get_balance
= 28
> account.get_balance
= 28
> account.get_balance
= 28
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 30
> account.get_balance
= 30
> account.rand_add
= 1
> account.get_balance
= 31
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 34
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 36
> account.get_balance
= 36
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 38
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 41
> account.rand_add
= 1
> account.get_balance
= 42
> account.get_balance
= 42
> account.rand_add
= 1
> account.get_balance
= 43
> account.rand_add
= 1
> account.get_balance
= 44
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 48
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 50
> account.get_balance
= 50
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 52
> account.get_balance
= 52
> account.get_balance
= 52
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.rand_add
= 1
> account.get_balance
= 56
test_completed

`ivy_check` complains if I remove the `x = 2` from the ensure so that code path of ivy does not seem to be effected.

Looking at the generated code it seems like https://xkcd.com/221/ was taken to literally.

int correct_rand::___ivy_choose(int rng,const char *name,int id) {
        return 0;
    }

unsigned correct_rand::ext__account__rand_add(){
    unsigned x;
    x = (unsigned)___ivy_choose(0,"fml:x",0);
    {
        int __tmp0;
        __tmp0 = (int)___ivy_choose(0,"___branch",14);
        if(__tmp0 == 0){
            x = (1 & 65535);
        }
        else {
            x = (2 & 65535);
        }
        account__balance = ((account__balance + x) & 65535);
    }
    return x;
}

https://github.com/kenmcmil/ivy/blob/master/ivy/ivy_to_cpp.py#L2862

Issue with skolemize tactic

The following used to work (e.g. with commit c740b27), but maybe for no good reason. With the lastest Ivy master, I get error: cannot infer sort of N in N.

https://github.com/stellar/scp-proofs/blob/e953816f587283184a8672e05f7de841b7c51d0e/1.7/SCP.ivy#L327

Anyway, I'm a bit confused by what the skolemize tactic does. The result seems significantly different if I just rename bound variables.
For example, compare:

property [cascade_accept_prepared] intact(N) & is_quorum(Q) & member(N,Q) & (forall N . intact(N) & member(N,Q) -> accepted_prepared(N,B,V)) -> (forall N . intact(N) -> accepted_prepared(N,B,V)) | (exists N2,S . intact(N2) & ~accepted_prepared(N2,B,V) & blocks_slices(S,N2) & (forall N3 . member(N3,S) -> (intact(N3) & accepted_prepared(N3,B,V))))
proof {
    tactic skolemize
    showgoals
}
theorem [protocol.liveness_step_1.additional_safety.cascade_accept_prepared] {
    individual _N : node
    function _N3(V0:nset) : node
    property exists N2,N,S. intact(N) & is_quorum(Q) & member(N,Q) & (intact(N) & member(N,Q) -> protocol.accepted_prepared(N,B,V)) -> (i
ntact(_N) -> protocol.accepted_prepared(_N,B,V)) | intact(N2) & ~protocol.accepted_prepared(N2,B,V) & blocks_slices(S,N2) & (member(_N3(S
),S) -> intact(_N3(S)) & protocol.accepted_prepared(_N3(S),B,V))
}

and

property [cascade_accept_prepared] intact(N) & is_quorum(Q) & member(N,Q) & (forall N2 . intact(N2) & member(N2,Q) -> accepted_prepared(N2,B,V)) -> (forall N3 . intact(N3) -> accepted_prepared(N3,B,V)) | (exists N4,S . intact(N4) & ~accepted_prepared(N4,B,V) & blocks_slices(S,N4) & (forall N5 . member(N5,S) -> (intact(N5) & accepted_prepared(N5,B,V))))
proof {
    tactic skolemize
    showgoals
}

theorem [protocol.liveness_step_1.additional_safety.cascade_accept_prepared] {
    individual _N3 : node
    function _N5(V0:nset) : node
    property exists N4,N2,S. intact(N) & is_quorum(Q) & member(N,Q) & (intact(N2) & member(N2,Q) -> protocol.accepted_prepared(N2,B,V)) -
> (intact(_N3) -> protocol.accepted_prepared(_N3,B,V)) | intact(N4) & ~protocol.accepted_prepared(N4,B,V) & blocks_slices(S,N4) & (member
(_N5(S),S) -> intact(_N5(S)) & protocol.accepted_prepared(_N5(S),B,V))
}

Am I misunderstanding something, or is there a problem with the skolemize tactic?

guidance in understanding `_generating` predicate functionality

I'm trying to impose some constraints on the type of data that's generated by ivy, for the following message structure.

#lang ivy1.7

include collections

type request_type = {req1, req2, req3}
type response_type = {res1, res2, res3, res4}

type packetIEID

type packet

object response_packet ={ 
	variant this of packet = struct{
		id : packetIEID,
		payload : response_type
	}

}

object request_packet ={ 
	variant this of packet= struct{
		id : packetIEID,
		payload : request_type
	}

}


object packet = {
	...

	action packet_handle(f:packet)
	
	before packet_handle{

		if _generating {
			assert
			(exists (X:response_packet) (f *> X)) |
			(exists (X:request_packet) (f *> X));


			if some (a:response_packet) f *> a {
				assert a.id = 7;
		
			};
			if some (s:request_packet) f *> s {
				assert s.id = 9;
		
			};

		}

	}

}


object response_packet = {
	...
	action packet_handle(f:response_packet)

	before packet_handle {
		if _generating{
			require f.id = 3;
		}
		#...
	}
	#...
}


object request_packet = {
	...
	action packet_handle(f:request_packet)

	before packet_handle{
		if _generating{
			require f.id = 5;
		}
		#...

	}
	#...
}

interpret request_type -> bv[16]
interpret response_type -> bv[8]
interpret packetIEID -> int

export packet.packet_handle

On compiling the data-structure file, and executing it, I get it to function the way I desire.

> packet.packet_handle({request_packet:{id:9,payload:req1}})
> packet.packet_handle({response_packet:{id:7,payload:res4}})
> packet.packet_handle({request_packet:{id:9,payload:req1}})
> packet.packet_handle({response_packet:{id:7,payload:res3}})
> packet.packet_handle({request_packet:{id:9,payload:req3}})
> packet.packet_handle({response_packet:{id:7,payload:res3}})
> packet.packet_handle({request_packet:{id:9,payload:req1}})


However, when I write another wrapper application that uses the data-structure message, with the following changes # export packet.packet_handle, as below:

#lang ivy1.7

include msg_struct


object intf = {
    action send(f:packet)
	action recv(f:packet)
    
    }


object spec = {
    relation sent(X:packet)

    after init {
        sent(X) := false
    }

    
    before intf.send{

        if _generating {
            assert
            (exists (X:response_packet) (f *> X)) |
            (exists (X:request_packet) (f *> X));


            if some (a:response_packet) f *> a {
                #a.id := 17;
                assert a.id = 17
        
            };
            if some (s:request_packet) f *> s {
                #s.id := 19;
                assert s.id = 19;
        
            };

        };

        sent(f) := true;
    }



    before intf.recv {
        assert sent(f)
    }

}

object protocol = {
    implement intf.send {
        call intf.recv(f)
    }
}



import intf.recv
export intf.send

I get the following undesireable output

> intf.send({response_packet:{id:0,payload:res1}})
< intf.recv({response_packet:{id:0,payload:res1}})
> intf.send({request_packet:{id:0,payload:req2}})
< intf.recv({request_packet:{id:0,payload:req2}})

The output is undesireable, as response_packet.id ~= 17 and request_packet.id ~= 19. Could you please advise me on the reason for this discrepancy?

Thank you.

TypeError: ivy_load_file() takes exactly 1 argument (2 given)

I followed the instruction in https://cs.stanford.edu/~padon/ivy/index.html#downloading.

When I run :
python ../../ivy/ivy2.py leader_election_ring.ivy

I get a type error:

TypeError                                 Traceback (most recent call last)
<ipython-input-1-5502424a515f> in <module>()
     18 ctx.__enter__()
     19 ivy_widget = AnalysisSessionWidget()
---> 20 session = AnalysisSession('leader_election_ring.ivy', ivy_widget)
     21 set_context(session)
     22 ivy_widget.transition_view.conjectures = session.analysis_state.ivy_interp.conjs[:]

/home/lsq/ivy/ivy/proof.pyc in __init__(self, filename, widget)
     62         self.filename = filename
     63         try:
---> 64             m = IvyModel(filename)
     65         except IvyError as e:
     66             raise e

/home/lsq/ivy/ivy/proof.pyc in __init__(self, filename)
     24         ag = ivy_new()
     25         try:
---> 26             ivy_load_file(open(filename), ag)
     27         except IvyError as e:
     28             e.filename = filename

TypeError: ivy_load_file() takes exactly 1 argument (2 given)

strange behavior with structs as member of parameterized object

Hello, Ivy find a counter-example to the following invariant, which seems strange to me:

#lang ivy1.7

type pair_t = struct {
    x : bool,
    y : bool
}

type index

object obj(i:index) = {
    individual p:pair_t
    after init {
        p.x := false;
        p.y := false;
    }
    export action act1 = {
        require p.x = false;
        p.y := true;
    }
    export action act2 = {
        require p.y = false;
        p.x := true;
    }
    invariant ~(p.x & p.y)
}

Installing ivy on M1 macOS Monterey 12.3

Hi All,

I installed ivy on a MacBook Pro (13-inch, M1, 2020) running MacOS Monterey 12.3 following the
instructions at:

https://github.com/kenmcmil/ivy/blob/master/doc/install.md

I tested on two different M1 machines one a clean install on MacOS Monterey 12.2 and the other an upgrade from Mojave to Monterey to 12.2. to 12.3. There was one Python issue on the upgrade machine with version 12.2.

Installation notes:

  1. needed to manually create the following link after installing openssl

sudo mkdir -p /usr/local/opt/openssl/lib
sudo ln -s /opt/local/libexec/openssl3/lib/libssl.3.dylib /usr/local/opt/openssl/lib/libssl.dylib

  1. git clone --recurse-submodules https://github.com/Microsoft/ivy.git needs to be changed to kenmcmil where it says Microsoft

Please make those two adjustments to the instructions (and add Monterey to title)

I re-ran all the models we have developed and didn't notice any issues. Previously on M1 we used the IA64 emulation path (rosetta), which worked without any issues, but the native build is significantly faster. The M1 machines have 16GB DRAM

Thanks,
Asgeir
[email protected]

AttributeError: 'IsolateDef' object has no attribute 'create_isolate'

I get the above error when trying to use ivy following the instructions at: http://microsoft.github.io/ivy/install.html

Command to trigger the error ivy doc/examples/client_server_example.ivy

The error occurs both when installing from source or installing the binary with pip; both on ubuntu 20.04 and an arch based system.

Full output below

Traceback (most recent call last):
  File "/usr/bin/ivy", line 10, in <module>
    sys.exit(main())
  File "/usr/lib/python2.7/site-packages/ivy/ivy.py", line 14, in main
    ui_main_loop(ivy_init())
  File "/usr/lib/python2.7/site-packages/ivy/ivy_init.py", line 106, in ivy_init
    source_file(fn,f,**skwargs)
  File "/usr/lib/python2.7/site-packages/ivy/ivy_init.py", line 72, in source_file
    ivy_load_file(f,**kwargs)
  File "/usr/lib/python2.7/site-packages/ivy/ivy_compiler.py", line 2301, in ivy_load_file
    ivy_compile(decls,**kwargs)
  File "/usr/lib/python2.7/site-packages/ivy/ivy_compiler.py", line 2239, in ivy_compile
    iso.create_isolate(isolate.get(),mod,**kwargs)
AttributeError: 'IsolateDef' object has no attribute 'create_isolate'

`ivy_to_cpp` and `ivyc` don't seem to handle enumerative interpretations correctly

#lang ivy1.7    
type id_t    
interpret id_t -> {id0, id1}    
object myObject = {    
    action myAction(x:id_t) = {    
        require x = id0;    
    }    
    invariant forall X:id_t. X = id0 | X = id1    
}    
export myObject.myAction    
extract runner = myObject

This can be verified by the command ivy_check test.ivy, and that makes me think that I don't have typos in this. However, when I convert this into C++ using ivyc target=test isolate=runner classname=test test.ivy, it creates C++ code that contains id0 in it without defining what id0 is. This leads to C++ compilation errors. For instance, the following is the error message that I got:

g++  -I /usr/local/lib/python2.7/dist-packages/ivy/include -L /usr/local/lib/python2.7/dist-packages/ivy/lib -Wl,-rpath=/usr/local/lib/python2.7/dist-packages/ivy/lib -g -o test test.cpp -lz3 -pthread
test.cpp: In member function ‘virtual void test::ext__myObject__myAction(unsigned int)’:
test.cpp:1115:26: error: ‘id0’ was not declared in this scope
 1115 |         ivy_assume((x == id0), "test.ivy: line 9");
     |                          ^~~
test.cpp: In member function ‘virtual bool ext__myObject__myAction_gen::generate(test&)’:
test.cpp:1198:19: error: ‘id0’ is not a member of ‘test’
 1198 |         x = test::id0;
      |      

Similarly, when I try to make executables using

ivy_to_cpp target=repl isolate=runner test.ivy    
g++ -O2 test.cpp -pthread -lpthread -o test

I also get an error of the same nature.

test.cpp: In member function ‘virtual void test::ext__myObject__myAction(unsigned int)’:
test.cpp:590:26: error: ‘id0’ was not declared in this scope
  590 |         ivy_assume((x == id0), "test.ivy: line 9");
      |                          ^~~

Understanding Interference in Ivy

Based on the example provided in Networking Tutorial

Script derived from above tutorial

#lang ivy1.7

type t

individual bit(X:t) : bool

module foomodule = {

    after init{
        bit(X) := true;
    }
    
    action flip(self:t) = {
        bit(self) := ~bit(self)
    }
}


instance foo(X:t) :foomodule
export foo.flip

extract iso_foo(me:t) = foo(me), bit(me)

The above script when verified using $ ivy_check study_interference.ivy, checks OK.
However, when attempting to generate C++ codes, it provides the following errors.

  1. Using target=test
$ ivyc isolate=iso_foo target=test study_interference.ivy 
error: cannot strip parameter t from bit

$ ivyc isolate=iso_foo target=test build=true study_interference.ivy 
error: cannot strip parameter t from bit

$ ivy_to_cpp isolate=iso_foo target=test study_interference.ivy 
error: cannot strip parameter t from bit

$ ivy_to_cpp isolate=iso_foo target=test build=true study_interference.ivy 
error: cannot strip parameter t from bit
  1. Using target=repl + with/without build=true
$ ivyc isolate=iso_foo target=repl build=true study_interference.ivy 
error: cannot strip parameter t from bit

$ ivyc isolate=iso_foo target=repl study_interference.ivy 
error: cannot strip parameter t from bit

$ ivy_to_cpp isolate=iso_foo target=repl build=true study_interference.ivy 
error: cannot strip parameter t from bit

$ ivy_to_cpp isolate=iso_foo target=repl study_interference.ivy 
error: cannot strip parameter t from bit

Script modified to custom representation

#lang ivy1.7

type t

module foomodule = {

    individual bit(X:t) : bool

    after init{
        bit(X) := true;
    }
    
    action flip(self:t) = {
        bit(self) := ~bit(self)
    }
}


instance foo(X:t) :foomodule
export foo.flip

extract iso_foo(me:t) = foo(me) 

ivy_checks OK.

$ ivy_check study_interference.ivy

Isolate this:

    The following action implementations are present:
        study_interference.ivy: line 14: implementation of foo.flip

    The following initializers are present:
        study_interference.ivy: line 9: foo.init[after1]

    Any assertions in initializers must be checked ... PASS

OK

However, when we attempt to compile this to cpp, we obtain some errors.

  1. Produces no messages on terminal. No executable is generated.
$ ivy_to_cpp isolate=iso_foo target=repl study_interference.ivy
  1. Produces an Executable. Can engage interactively from terminal.
$ ivy_to_cpp isolate=iso_foo target=repl build=true study_interference.ivy
g++   -g -o study_interference study_interference.cpp -pthread

$ ./study_interference 1
> foo.flip(1)
> foo.flip(3)
> foo.flip(43)
> 

  1. Compilation error when using build=true and either of ivyc or ivy_to_cpp
$ ivy_to_cpp isolate=iso_foo target=test build=true study_interference.ivy 
g++  -I /home/user/.local/lib/python2.7/site-packages/ivy/include -L /home/user/.local/lib/python2.7/site-packages/ivy/lib -Wl,-rpath=/home/user/.local/lib/python2.7/site-packages/ivy/lib -g -o study_interference study_interference.cpp -lz3 -pthread
study_interference.cpp: In member function ‘virtual void ext__foo__flip_gen::execute(study_interference&)’:
study_interference.cpp:1244:35: error: no matching function for call to ‘study_interference::ext__foo__flip(int&, int&)’
     obj.ext__foo__flip(prm__X,self);
                                   ^
study_interference.cpp:1113:6: note: candidate: virtual void study_interference::ext__foo__flip(int)
 void study_interference::ext__foo__flip(int self){
      ^~~~~~~~~~~~~~~~~~
study_interference.cpp:1113:6: note:   candidate expects 1 argument, 2 provided
$ ivyc isolate=iso_foo target=test build=true study_interference.ivy 
g++  -I /home/user/.local/lib/python2.7/site-packages/ivy/include -L /home/user/.local/lib/python2.7/site-packages/ivy/lib -Wl,-rpath=/home/user/.local/lib/python2.7/site-packages/ivy/lib -g -o study_interference study_interference.cpp -lz3 -pthread
study_interference.cpp: In member function ‘virtual void ext__foo__flip_gen::execute(study_interference&)’:
study_interference.cpp:1244:35: error: no matching function for call to ‘study_interference::ext__foo__flip(int&, int&)’
     obj.ext__foo__flip(prm__X,self);
                                   ^
study_interference.cpp:1113:6: note: candidate: virtual void study_interference::ext__foo__flip(int)
 void study_interference::ext__foo__flip(int self){
      ^~~~~~~~~~~~~~~~~~
study_interference.cpp:1113:6: note:   candidate expects 1 argument, 2 provided

I'd appreciate some help in understanding how to interpret these errors.

`ivy_to_cpp` generates invalid C++

I ran ivy_to_cpp target=test isolate=executable_runner build=false executable.ivy for the following ivy code

#lang ivy1.7

type id_t
type val_t
type counter_t
type statement_t = {prepare_st, commit_st}

interpret id_t -> bv[2]
interpret val_t -> bv[4]
interpret counter_t -> bv[8]

object network = {
    # Removing this struct definition removes the compilation bug.
    <<< header
    struct %`node.started_ballot_protocol`;
    >>>
}

object node(self:id_t) = {
    relation voted(TYPE:statement_t, CTR:counter_t, VAL:val_t)
    import action started_ballot_protocol(n:counter_t, x:val_t)
    after started_ballot_protocol {
        # Removing this `require` statement removes the compilation bug.
        require voted(prepare_st, n, x) = true;
    }
    after init {
        # Removing this initialization removes the compilation bug.
        voted(TYPE, X, V) := false;
    }
}

extract executable_runner = network, node

and tried to compile it using clang++ -c -O2 -g -std=c++17 -pthread -I /home/hidenori/ivy/ivy/include -o executable.o executable.cpp.

However, this failed with

executable.cpp:1279:20: error: use of undeclared identifier '__tup__unsigned__unsigned__statement_t'
        hash_thunk<__tup__unsigned__unsigned__statement_t,bool> __tmp0;
                   ^
executable.cpp:1283:9: error: use of undeclared identifier '__tmp0'
        __tmp0[executable::__tup__unsigned__unsigned__statement_t(V,X,TYPE)] = false;
        ^
executable.cpp:1283:28: error: no member named '__tup__unsigned__unsigned__statement_t' in 'executable'
        __tmp0[executable::__tup__unsigned__unsigned__statement_t(V,X,TYPE)] = false;
               ~~~~~~~~~~~~^
executable.cpp:1290:21: error: no matching constructor for initialization of 'executable::__tup__unsigned__statement_t__unsigned__unsigned'
        node__voted[executable::__tup__unsigned__statement_t__unsigned__unsigned(prm__V0,TYPE,X,V)] = __tmp0[executable::__tup__unsigned__unsigned__statement_t(V,X,TYPE)];
                    ^                                                            ~~~~~~~~~~~~~~~~
./executable.h:644:53: note: candidate constructor not viable: no known conversion from 'int' to 'const executable::statement_t' for 2nd argument
__tup__unsigned__statement_t__unsigned__unsigned(){}__tup__unsigned__statement_t__unsigned__unsigned(const unsigned &arg0,const statement_t &arg1,const unsigned &arg2,const unsigned &arg3) : arg0(arg0),arg1(arg1),arg2(arg2),arg3(arg3){}
                                                    ^
./executable.h:639:8: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 4 were provided
struct __tup__unsigned__statement_t__unsigned__unsigned {
       ^
./executable.h:639:8: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 4 were provided
./executable.h:644:1: note: candidate constructor not viable: requires 0 arguments, but 4 were provided
__tup__unsigned__statement_t__unsigned__unsigned(){}__tup__unsigned__statement_t__unsigned__unsigned(const unsigned &arg0,const statement_t &arg1,const unsigned &arg2,const unsigned &arg3) : arg0(arg0),arg1(arg1),arg2(arg2),arg3(arg3){}
^
executable.cpp:1290:103: error: use of undeclared identifier '__tmp0'
        node__voted[executable::__tup__unsigned__statement_t__unsigned__unsigned(prm__V0,TYPE,X,V)] = __tmp0[executable::__tup__unsigned__unsigned__statement_t(V,X,TYPE)];
                                                                                                      ^
executable.cpp:1290:122: error: no member named '__tup__unsigned__unsigned__statement_t' in 'executable'
        node__voted[executable::__tup__unsigned__statement_t__unsigned__unsigned(prm__V0,TYPE,X,V)] = __tmp0[executable::__tup__unsigned__unsigned__statement_t(V,X,TYPE)];
                                                                                                             ~~~~~~~~~~~~^
6 errors generated.

It looks like Ivy is confused about the arity of the relation voted. For some reason, as denoted in comments, removing parts of the code makes the bug disappear.

Ivy proves liveness property which I think should not hold

Here is an example that looks unsound to me. Am I missing something?

#lang ivy1.7

relation bit

export action a = {
    bit := *;
}

temporal property (eventually bit) -> (globally eventually bit)
proof {
    tactic l2s with
        invariant eventually bit
        invariant eventually globally ~bit
        invariant ($l2s_w . globally ~bit) | (globally ~bit) | globally (eventually bit)
}

Error in Randomized generation of data-types with multiple levels of depth

There seems to be a bug in the compiler that implements the __randomize() funciton for data types that contain >2 levels of depth.

I'm attempting to generate messages with the following levels of hierarchy:

#lang ivy1.7

include collections

type bit
type byte 
type bytes

type int

type levelD = {d1, d2, d3, d4}
type levelE = {e1, e2, e3}



type levelA = struct{
	idA : int,
	valueA : bit
}

type levelB = struct{
	idB : int,
	valueB: byte
}

type levelC = struct{
	idC : int,
	valueC : bytes
}



# -------------------------------
type level4_t



object level4A_t ={
	variant this of level4_t = struct{
		id4A : int, 
		value4A : levelD
	}
}


object level4B_t ={
	variant this of level4_t = struct{
		id4B : int, 
		value4B : levelE
	}
}

# --------------------------------
type level3_t

object level3_A = {
	variant this of level3_t = struct{
		id3 : int, 
		value3 :levelA 
	}
}


#instance level3_B : level3(levelB)
object level3_B = {
	variant this of level3_t = struct{
		id3 : int, 
		value3 :levelB 
	}
}


#instance level3_C : level3(levelC)
object level3_C = {
	variant this of level3_t = struct{
		id3 : int, 
		value3 :levelC 
	}
}


#instance level3_level4 : level3(level4_t)

object level3_level4 = {
	variant this of level3_t = struct{
		id3 : int, 
		value3 :level4_t 
	}
}

# --------------------------------
type level2_t

object level2_messageA_t  ={
	variant this of level2_t = struct{
		id2 : int,
		criticality: bit,
		value : level3_t
	}
}

object level2_messageB_t  ={
	variant this of level2_t = struct{
		id2 : int,
		criticality: bit,
		value : levelD
	}
}

object level2_messageC_t  ={
	variant this of level2_t = struct{
		id2 : int,
		criticality: bit,
		value : levelE
	}
}


# --------------------------------------------
type level1_t

object level1_message_t = {
	variant this of level1_t = struct{
		id1 : int,
		list1 : vector[level2_t]
	}
}



object level1_msgA_t = {
	variant this of level1_t = struct{
		id1 : int,
		list1 : vector[levelA]
	}
}


object level1_msgB_t = {
	variant this of level1_t = struct{
		id1 : int,
		list1 : vector[levelB]
	}
}

# --------------------------------------------
type level0_t

object level0_message_t = {
	variant this of level0_t= struct{
		id0 : int,
		value0: level1_message_t
	}
}

When I attempt to use the random generator to create random instances of a message of type level0_t, I obtain the following sample messages

> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageC_t:{id2:0,criticality:0,value:e1}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}},{level2_messageC_t:{id2:0,criticality:0,value:e1}},{level2_messageA_t:{id2:0,criticality:0,value:{}}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageB_t:{id2:0,criticality:0,value:d3}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageC_t:{id2:0,criticality:0,value:e3}},{level2_messageA_t:{id2:0,criticality:0,value:{}}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageC_t:{id2:0,criticality:0,value:e3}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}},{level2_messageB_t:{id2:0,criticality:0,value:d3}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageB_t:{id2:0,criticality:0,value:d1}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}},{level2_messageC_t:{id2:0,criticality:0,value:e3}},{level2_messageA_t:{id2:0,criticality:0,value:{}}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}},{level2_messageA_t:{id2:0,criticality:0,value:{}}},{level2_messageB_t:{id2:0,criticality:0,value:d2}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}},{level2_messageC_t:{id2:0,criticality:0,value:e3}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageC_t:{id2:0,criticality:0,value:e2}},{level2_messageC_t:{id2:0,criticality:0,value:e1}},{level2_messageA_t:{id2:0,criticality:0,value:{}}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageC_t:{id2:0,criticality:0,value:e1}},{level2_messageC_t:{id2:0,criticality:0,value:e3}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageC_t:{id2:0,criticality:0,value:e1}},{level2_messageB_t:{id2:0,criticality:0,value:d3}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageB_t:{id2:0,criticality:0,value:d4}},{level2_messageC_t:{id2:0,criticality:0,value:e3}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageC_t:{id2:0,criticality:0,value:e1}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageB_t:{id2:0,criticality:0,value:d3}},{level2_messageC_t:{id2:0,criticality:0,value:e1}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}}]}}})
> intf.send({level0_message_t:{id0:0,value0:{id1:0,list1:[{level2_messageA_t:{id2:0,criticality:0,value:{}}}]}}})

Note that the all instances of level2_messageA_t has its value: {}.
The compiler generated cpp codebase has the following lines, which indicates that level2_message_t.value is not being randomly generated.

template <>
void  __randomize<message_app::level2_messageA_t>( gen &g, const  z3::expr &v){
    __randomize<int>(g,g.apply("level2_messageA_t.id2",v));
    __randomize<int>(g,g.apply("level2_messageA_t.criticality",v));
}

However, the corresponding __from_solver functionality has the following structure, where we see that all three components of the data-type are being processes.

template <>
void  __from_solver<message_app::level2_messageA_t>( gen &g, const  z3::expr &v,message_app::level2_messageA_t &res){
    __from_solver(g,g.apply("level2_messageA_t.id2",v),res.id2);
    __from_solver(g,g.apply("level2_messageA_t.criticality",v),res.criticality);
    __from_solver(g,g.apply("level2_messageA_t.value",v),res.value);
}

Is there a way in which the compiler is constrained to handle random-generation only upto 2 depth-levels?

compiler error in ivy1.8 : invalid conversion from ‘int’ to

The issue I've indicated below is similar to the one that was raised earlier in #57. It provides a more concise representation of the problem.

The ivy-script for modelling a connection-mgmt protocol check correctly, withivy_check OK.
However, when compiling it using ivyc target=test conn-mgmt.ivy, we obtain the following error:

$ ivyc target=test connection_mgmt_v2.ivy 
g++ -Wno-parentheses-equality  -std=c++11  -I /usr/local/lib/python2.7/dist-packages/ivy/include -L /usr/local/lib/python2.7/dist-packages/ivy/lib -Xlinker -rpath -Xlinker /usr/local/lib/python2.7/dist-packages/ivy/lib -g -o connection_mgmt_v2 connection_mgmt_v2.cpp -lz3 -pthread
connection_mgmt_v2.cpp: In member function ‘void connection_mgmt_v2::__init()’:
connection_mgmt_v2.cpp:1863:20: error: ‘__tup__namesOfProcedures__int’ was not declared in this scope
         hash_thunk<__tup__namesOfProcedures__int,bool> __tmp3;
                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
connection_mgmt_v2.cpp:1863:20: note: suggested alternative: ‘__tup__int__namesOfProcedures’
         hash_thunk<__tup__namesOfProcedures__int,bool> __tmp3;
                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    __tup__int__namesOfProcedures
connection_mgmt_v2.cpp:1863:54: error: template argument 1 is invalid
         hash_thunk<__tup__namesOfProcedures__int,bool> __tmp3;
                                                      ^
connection_mgmt_v2.cpp:1863:54: error: template argument 3 is invalid
connection_mgmt_v2.cpp:1866:36: error: ‘__tup__namesOfProcedures__int’ is not a member of ‘connection_mgmt_v2’
         __tmp3[connection_mgmt_v2::__tup__namesOfProcedures__int(Y,X)] = false;
                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
connection_mgmt_v2.cpp:1871:98: error: invalid conversion from ‘int’ to ‘connection_mgmt_v2::namesOfProcedures’ [-fpermissive]
         client__intf__connection_procedures[connection_mgmt_v2::__tup__int__namesOfProcedures(X,Y)] = __tmp3[connection_mgmt_v2::__tup__namesOfProcedures__int(Y,X)];
                                                                                                  ^
In file included from connection_mgmt_v2.cpp:1:0:
connection_mgmt_v2.h:714:34: note:   initializing argument 2 of ‘connection_mgmt_v2::__tup__int__namesOfProcedures::__tup__int__namesOfProcedures(const int&, const connection_mgmt_v2::namesOfProcedures&)’
 __tup__int__namesOfProcedures(){}__tup__int__namesOfProcedures(const int &arg0,const namesOfProcedures &arg1) : arg0(arg0),arg1(arg1){}
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
connection_mgmt_v2.cpp:1871:130: error: ‘__tup__namesOfProcedures__int’ is not a member of ‘connection_mgmt_v2’
     client__intf__connection_procedures[connection_mgmt_v2::__tup__int__namesOfProcedures(X,Y)] = __tmp3[connection_mgmt_v2::__tup__namesOfProcedures__int(Y,X)];
                                                                                                                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At global scope:
cc1plus: warning: unrecognized command line option ‘-Wno-parentheses-equality’

Unexpected behavior with a macro inside an if statement

The following code throws a CTI with my_obj.always_false_macro(0) = true in the pre-state, which doesn't make much sense.

#lang ivy1.7

type my_type

object my_obj =  {
    relation relation_that_we_never_update(SOMETHING:my_type)

    after init {
        relation_that_we_never_update(X) := true;
    }

    relation always_false_macro(SOMETHING:my_type)
    definition always_false_macro(something:my_type) = false

    action my_action_macro(something:my_type) = {
        if (always_false_macro(something)) {
            # We should never get here because `always_false_macro` is always false.
            # But Ivy seems to get confused and think that this is reachable.
            relation_that_we_never_update(something) := false;
        }
    }
}


# This should always be true since
# (1) We set relation_that_we_never_update to be `true` in the initialization.
# (2) We never update this relation.
invariant forall X. my_obj.relation_that_we_never_update(X)

export my_obj.my_action_macro

How to debug the following `ivy_check` generated error?

While using ivy v1.7 and v1.8 and using ivy_check <filename.ivy> we obtain the following error:

Isolate this:
...
RuntimeError: maximum recursion depth exceeded while calling a Python object

The error lines indicated in ... can be accessed at the following gist. An abridged version of it is provided below

  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 675, in int_update
    thing = op.int_update(domain,pvars);
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 1083, in int_update
    v = self.apply_actuals(domain,pvars,v)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 1126, in apply_actuals
    res = res.int_update(domain,pvars)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 675, in int_update
    thing = op.int_update(domain,pvars);
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 1054, in int_update
    return bind_olds_action(self.args[0].int_update(domain,pvars))
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 675, in int_update
    thing = op.int_update(domain,pvars);
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 712, in int_update
    foo = a.int_update(domain, pvars)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 675, in int_update
    thing = op.int_update(domain,pvars);
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 1083, in int_update
    v = self.apply_actuals(domain,pvars,v)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_actions.py", line 1104, in apply_actuals
    v = substitute_constants_ast(v,subst)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in substitute_constants_ast
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_ast.py", line 20, in clone
    res = type(self)(*args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in <genexpr>
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in substitute_constants_ast
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_ast.py", line 20, in clone
    res = type(self)(*args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in <genexpr>
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in substitute_constants_ast
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_ast.py", line 20, in clone
    res = type(self)(*args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in <genexpr>
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in substitute_constants_ast
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_ast.py", line 20, in clone
    res = type(self)(*args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in <genexpr>
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in substitute_constants_ast
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_ast.py", line 20, in clone
    res = type(self)(*args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in <genexpr>
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in substitute_constants_ast
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_ast.py", line 20, in clone
    res = type(self)(*args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in <genexpr>
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in substitute_constants_ast
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_ast.py", line 20, in clone
    res = type(self)(*args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in <genexpr>
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in substitute_constants_ast
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_ast.py", line 20, in clone
    res = type(self)(*args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in <genexpr>
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in substitute_constants_ast
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_ast.py", line 234, in clone
    return type(self)(self.rep,list(args))
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 178, in <genexpr>
    return ast.clone(substitute_constants_ast(x,subs) for x in ast.args)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_logic_utils.py", line 176, in substitute_constants_ast
    return subs.get(ast.rep,ast)
  File "<string>", line 48, in __hash__

mod in Ivy

Modulo in ivy has a correctness bug. In numbers.ivy and order.ivy, mod is defined as

mod(X,Y) = X - X / Y * X

When it should instead be defined as

mod(Y,X) = X - X / Y * Y

(For example, mod(3, 2) should equal 3 - (3 / 2) * 2 = 3 - 1 * 2 = 1, not 3 - (3 / 2) * 3 = 3 - 1 * 3 = 0.

Here is an example using ivy to reproduce the bug:

#lang ivy1.8

include numbers
include order

process p = {
    export action take_mod(y:nat,x:nat)  # Computes y % x
    implement take_mod {
        require x ~= 0;

        var rem : nat;
        rem := y.mod(x);
        debug "Taking Mod" with dividend = y,
            divisor = x,
            remainder = rem
            ;
    }
    export action mod_table(upto:nat,by:nat)  # prints y % by for y = 0,1,...,upto-1
    implement mod_table {
        var dividend :nat;
        dividend := 0;
        while dividend < upto {
            take_mod(dividend, by);
            dividend := dividend.next;
        }
    }
} with nat

Running the above program, I can produce the following output:

> p.mod_table(4, 2)
{
    "event" : "Taking Mod",
    "dividend" : 0,
    "divisor" : 2,
    "remainder" : 0,
}
{
    "event" : "Taking Mod",
    "dividend" : 1,
    "divisor" : 2,
    "remainder" : 1,
}
{
    "event" : "Taking Mod",
    "dividend" : 2,
    "divisor" : 2,
    "remainder" : 0,
}
{
    "event" : "Taking Mod",
    "dividend" : 3,
    "divisor" : 2,
    "remainder" : 0,
}

Range & enumerative interpretation of a sort does not seem to work

#lang ivy1.7    
    
type id_t    
    
interpret id_t -> bv[1] # Works as expected    
   
property [simple_property] forall N:id_t. N = 0 | N = 1

When checking the code above using ivy_check, it verifies the property as expected.

$ ivy_check interpretation.ivy 

Isolate this:

    The following properties are to be checked:
        interpretation.ivy: line 9: simple_property ... PASS

    The following temporal property is being proved:

        interpretation.ivy: line 9: simple_property

OK

However, when I replace the interpretation with each of the following, I get error: Cannot cast "0:id_t" to native sort id_t

  • interpret id_t -> {0..1}
  • interpret id_t -> {0, 1}

Due to this, all sorts interpretations I have end up having 2^n elements for some n, but if I could use another number, it would be very useful!

Options to view results

I've been working on evaluating this as one tool for testing during QUIC development and I've had trouble getting to actually view results. Is using Tkinter the only option? Are there any screenshots or representations of what this will look like?

I am currently unable to use X11 Forwarding due to system issues and so, it seems I would need to have a local repo built along with one on the server, copy the results over and view them using TKinter on my local laptop. Which isn't great for automation and sharing with a development team.

Is there a way to view results using html/css etc.?

Message structures compile correctly, but the up-cast/down-cast functionality of generator causes errors

The following message structure compiles correctly in the ivy-compiler.

#lang ivy1.7

type choice_response_request_type

object response_type_num = {
	variant this of choice_response_request_type
}

object request_type_num = {
	variant this of choice_response_request_type
}


type message_block

type response_type = {res1, res2, res3}
type request_type = {req1, req2, req3, req4}

object response_block = {
	variant this of message_block = struct{
		load : response_type,
		val : choice_response_request_type
	}
}

object request_block = {
	variant this of message_block = struct{
		load : request_type,
		val : choice_response_request_type
	}
}


interpret choice_response_request_type -> int
interpret response_type_num -> int
interpret request_type_num -> int

However, when the above messages are used inside an application, the generator throws up an error. From the logs, I feel there an error in the up-cast/down-cast of choice_response_request_type into either response_type_num or request_type_num.

I obtain the following logs:

ivyc target=test message_app10A.ivy 
Traceback (most recent call last):
  File "/home/user/.local/bin/ivyc", line 11, in <module>
    sys.exit(ivyc())
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_to_cpp.py", line 5717, in ivyc
    main_int(True)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_to_cpp.py", line 5838, in main_int
    header,impl = module_to_cpp_class(classname,basename)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_to_cpp.py", line 3354, in module_to_cpp_class
    emit_action_gen(sf,impl,name,action,classname)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_to_cpp.py", line 1740, in emit_action_gen
    impl.append('add("(assert {})");\n'.format(slv.formula_to_z3(pre).sexpr().replace('|!1','!1|').replace('\\|','').replace('\n',' "\n"')))
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_solver.py", line 536, in formula_to_z3
    z3_fmla = formula_to_z3_closed(fmla)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_solver.py", line 525, in formula_to_z3_closed
    z3_formula = formula_to_z3_int(fmla)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_solver.py", line 494, in formula_to_z3_int
    args = [formula_to_z3_int(arg) for arg in fmla.args]
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_solver.py", line 494, in formula_to_z3_int
    args = [formula_to_z3_int(arg) for arg in fmla.args]
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_solver.py", line 494, in formula_to_z3_int
    args = [formula_to_z3_int(arg) for arg in fmla.args]
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_solver.py", line 494, in formula_to_z3_int
    args = [formula_to_z3_int(arg) for arg in fmla.args]
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_solver.py", line 491, in formula_to_z3_int
    return atom_to_z3(fmla)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_solver.py", line 407, in atom_to_z3
    rel = lookup_native(atom.relname,relations,"relation")
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_solver.py", line 247, in lookup_native
    raise iu.IvyError(None,'{} is not a supported Z3 {}'.format(name,kind))
NameError: global name 'name' is not defined

for the application developed below:

#lang ivy1.7

include messageIEC


object intf = {
    action send(x:message_block)
	action recv(x:message_block)
    
    }


object spec = {
    relation sent(X:message_block)

    after init {
        sent(X) := false
    }

    before intf.send {
        sent(x) := true
    }

    before intf.recv {
        assert sent(x)
    }
}

object protocol = {
    implement intf.send {
        call intf.recv(x)
    }
}

import intf.recv
export intf.send

Assumption failed in tcp_test

On commit 6f081c8c93 I'm observing an assertion failure in tcp_test:

➜  sandbox git:(main) ✗ ~/bin/ivyc target=test echo.ivy && ivy_launch cid=1 sid=1  echo

g++ -Wno-parentheses-equality  -std=c++11  -I /Users/ntaylor/code/ivy/ivy/include -L /Users/ntaylor/code/ivy/ivy/lib -Xlinker -rpath -Xlinker /Users/ntaylor/code/ivy/ivy/lib -I /usr/local/opt/openssl/include -L /usr/local/opt/openssl/lib -Xlinker -rpath -Xlinker /usr/local/opt/openssl/lib -fno-limit-debug-info -g -o echo echo.cpp -lz3 -pthread
ld: warning: directory not found for option '-L/usr/local/opt/openssl/lib'
`ivy_shell`; lldb -- ./echo 1 1 "[[0,{addr:0x7f000001,port:49124}],[1,{addr:0x7f000001,port:49125}]]" "[[0,{addr:0x7f000001,port:49126}],[1,{addr:0x7f000001,port:49127}]]"
NBT: PIDs: 39000
(lldb) target create "./echo"
Current executable set to '/Users/ntaylor/school/phd/projects/ivy_synthesis/sandbox/echo' (arm64).
(lldb) settings set -- target.run-args  "1" "1" "[[0,{addr:0x7f000001,port:49124}],[1,{addr:0x7f000001,port:49125}]]" "[[0,{addr:0x7f000001,port:49126}],[1,{addr:0x7f000001,port:49127}]]"
(lldb) process launch
Process 39008 launched: '/Users/ntaylor/school/phd/projects/ivy_synthesis/sandbox/echo' (arm64)
> client.server.ping(1,0,216)
< client.server.sock.send(1,{addr:2130706433,port:49124},{kind:ping_kind,sid:1,cid:0,val:216})
> client.server.ping(1,1,200)
< client.server.sock.send(1,{addr:2130706433,port:49125},{kind:ping_kind,sid:1,cid:1,val:200})
> client.server.ping(0,0,170)
< client.server.sock.send(0,{addr:2130706433,port:49124},{kind:ping_kind,sid:0,cid:0,val:170})
> client.sock.recv(0,{addr:2130706433,port:49126},{kind:pong_kind,sid:0,cid:0,val:170})
assumption_failed("/Users/ntaylor/code/ivy/ivy/include/1.8/network.ivy: line 287")
/Users/ntaylor/code/ivy/ivy/include/1.8/network.ivy: line 287: error: assumption failed
Process 39008 exited with status = 1 (0x00000001)

The offending line appears to be in tcp_test's ghost code for before recv:

284                     before recv {
285                         require net.len(src,id) > 0;
286                         var thing := net.queue(src,id);
287                         require msg = thing.value(0);
288                     }

The failing program is as follows:

➜  sandbox git:(main) ✗ cat echo.ivy
#lang ivy1.8

# An echo server implemented in Ivy.
# author: ntaylor

include collections
include network
include numbers

global {
    type client_id = {0..cid}
    type server_id = {0..sid}

    alias byte = uint[8]

    type msg_kind = {
        ping_kind,
        pong_kind
    }

    class msg_t = {
        field kind : msg_kind
        field sid  : server_id
        field cid  : client_id
        field val  : byte
    }

    instance net: tcp_test.net(msg_t)
}

process client(self: client_id) = {
    instance sock : net.socket

    common {
        specification {
            # if a ping to client C from server S is not in flight at the moment
            relation in_flight(S: server_id, C: client_id)

            # - the value if a server S has pinged client C with a value and
            # we have not yet received the response.
            var pinged(S: server_id, C: client_id): byte

            before server.ping(s: server_id, c: client_id, val: byte) {
                require ~in_flight(s,c);
                in_flight(s,c) := true;

                pinged(s, c) := val;
            }

            before server.sock.send(s: server_id, src:tcp.endpoint, msg: msg_t) {
                # Why might this make BMC hang?
                #require msg.sid = s;
            }

            before server.sock.recv(s: server_id, src:tcp.endpoint, msg: msg_t) {
                in_flight(s,msg.cid) := false;
            }

            before client.sock.recv(c: client_id, src:tcp.endpoint, msg: msg_t) {
                require c = msg.cid;
                require in_flight(msg.sid,msg.cid);
                require pinged(msg.sid,msg.cid) = msg.val;
            }
        }
    }


    implementation {
        implement sock.recv(src:tcp.endpoint, msg: msg_t) {
            sock.send(src, msg)
        }
    }

    common {
        implementation {
            process server(self: server_id) = {

                export action ping(c: client_id, v: byte)
                import action pong(c: client_id, v: byte)

                instance sock : net.socket

                implement ping {
                    var m: msg_t;
                    m.sid := self;
                    m.cid := c;
                    m.val := v;

                    sock.send(client(c).sock.id, m);
                }

                implement sock.recv(src:tcp.endpoint, msg: msg_t) {
                    pong(msg.cid, msg.val)
                }
            }
        }
    }
}

axiom client(X).sock.id = client(Y).sock.id -> X = Y
attribute method = bmc[10]

Z3 exception with an empty subclass

Given the following simplified code:

123     class man_msg_t = {
124         action handle(self: manager_id, ^msg: man_msg_t)
125     }
126
...
154
155     # An unpark message is sent to a node when execution can resume.
156     subclass unpark of man_msg_t = {
157         action handle(self: manager_id, ^msg: unpark)
158     }

Ivy throws the following z3 exception:

~/code/ivy-ts(main ✗) make
cd src/; ivyc target=test server.ivy
Traceback (most recent call last):
  File "/Users/ntaylor/bin/ivyc", line 11, in <module>
    load_entry_point('ms-ivy', 'console_scripts', 'ivyc')()
  File "/Users/ntaylor/code/ivy/ivy/ivy_to_cpp.py", line 5641, in ivyc
    main_int(True)
  File "/Users/ntaylor/code/ivy/ivy/ivy_to_cpp.py", line 5775, in main_int
    header,impl = module_to_cpp_class(classname,basename)
  File "/Users/ntaylor/code/ivy/ivy/ivy_to_cpp.py", line 2873, in module_to_cpp_class
    emit_action_gen(sf,impl,name,action,classname)
  File "/Users/ntaylor/code/ivy/ivy/ivy_to_cpp.py", line 1201, in emit_action_gen
    impl.append('add("(assert {})");\n'.format(slv.formula_to_z3(pre).sexpr().replace('|!1','!1|').replace('\\|','').replace('\n',' "\n"')))
  File "/Users/ntaylor/code/ivy/ivy/ivy_solver.py", line 599, in formula_to_z3
    z3_fmla = formula_to_z3_closed(fmla)
  File "/Users/ntaylor/code/ivy/ivy/ivy_solver.py", line 588, in formula_to_z3_closed
    z3_formula = formula_to_z3_int(fmla)
  File "/Users/ntaylor/code/ivy/ivy/ivy_solver.py", line 557, in formula_to_z3_int
    args = [formula_to_z3_int(arg) for arg in fmla.args]
  File "/Users/ntaylor/code/ivy/ivy/ivy_solver.py", line 557, in formula_to_z3_int
    args = [formula_to_z3_int(arg) for arg in fmla.args]
  File "/Users/ntaylor/code/ivy/ivy/ivy_solver.py", line 557, in formula_to_z3_int
    args = [formula_to_z3_int(arg) for arg in fmla.args]
  File "/Users/ntaylor/code/ivy/ivy/ivy_solver.py", line 557, in formula_to_z3_int
    args = [formula_to_z3_int(arg) for arg in fmla.args]
  File "/Users/ntaylor/code/ivy/ivy/ivy_solver.py", line 554, in formula_to_z3_int
    return atom_to_z3(fmla)
  File "/Users/ntaylor/code/ivy/ivy/ivy_solver.py", line 464, in atom_to_z3
    return apply_z3_func(pred,tup)
  File "/Users/ntaylor/code/ivy/ivy/ivy_solver.py", line 346, in apply_z3_func
    fact = z3_to_expr_ref(z3.Z3_mk_app(pred.ctx_ref(), pred.ast, sz, _args), pred.ctx)
  File "/Users/ntaylor/code/ivy/ivy/z3/z3core.py", line 1565, in Z3_mk_app
    _elems.Check(a0)
  File "/Users/ntaylor/code/ivy/ivy/z3/z3core.py", line 1336, in Check
    raise self.Exception(self.get_error_message(ctx, err))
ivy.z3.z3types.Z3Exception: Sort mismatch at argument #2 for function (declare-fun |*>:manager.man_msg_t:manager.unpark|
             (manager.man_msg_t manager.unpark)
             Bool) supplied sort is Int
make: *** [src/server] Error 1
~/code/ivy-ts(main ✗)

Adding an unused field in the subclass alleviates the issue, however.

Model Checking Comparison Operation

When using attribute method = mc with bit vector types (of any length), IVy claims correctness of the arguments n > n and n < n as if they were written n >= n and n <= n, respectively. This has been our observation on values from 0 to 31, including in the following small example.

#lang ivy1.7

attribute method = mc

type number
interpret number -> bv[4]

var test : number

after init {
  test := 1;
}

invariant test < 1

When verified using ivy_check, the above model passes all checks.

Bounded Verification Feature

Section 2.2 of the PLDI'16 paper describes a model checking functionality ion Ivy to rule out some protocol bugs before the inductive invariant search phase.
However, I can't find any commands to do this bounded model checking. Is this feature still available?

ivy_to_cpp should mangle classnames derived from filenames

At present, one can't compile, for instance, an ivy file named time.ivy:

(venv) ➜  accord_ivy git:(main) ✗ cat walltime.ivy
#lang ivy1.8

# Some functionality for timestamp operations.

include collections

global {
}

module foo = {
}

(venv) ➜  accord_ivy git:(main) ✗ ivyc target=test walltime.ivy
g++ -Wno-parentheses-equality  -std=c++11  -I /Users/ntaylor/code/ivy/ivy/include -L /Users/ntaylor/code/ivy/ivy/lib -Xlinker -rpath -Xlinker /Users/ntaylor/code/ivy/ivy/lib -I /Users/ntaylor/code/ivy/submodules/z3/src/api/python/z3/include -L /Users/ntaylor/code/ivy/submodules/z3/src/api/python/z3/lib -Xlinker -rpath -Xlinker /Users/ntaylor/code/ivy/submodules/z3/src/api/python/z3/lib -I /usr/local/opt/openssl/include -L /usr/local/opt/openssl/lib -Xlinker -rpath -Xlinker /usr/local/opt/openssl/lib -g -o walltime walltime.cpp -lz3 -pthread
ld: warning: directory not found for option '-L/usr/local/opt/openssl/lib'
(venv) ➜  accord_ivy git:(main) ✗
(venv) ➜  accord_ivy git:(main) ✗ cp walltime.ivy time.ivy
(venv) ➜  accord_ivy git:(main) ✗ ivyc target=test time.ivy
g++ -Wno-parentheses-equality  -std=c++11  -I /Users/ntaylor/code/ivy/ivy/include -L /Users/ntaylor/code/ivy/ivy/lib -Xlinker -rpath -Xlinker /Users/ntaylor/code/ivy/ivy/lib -I /Users/ntaylor/code/ivy/submodules/z3/src/api/python/z3/include -L /Users/ntaylor/code/ivy/submodules/z3/src/api/python/z3/lib -Xlinker -rpath -Xlinker /Users/ntaylor/code/ivy/submodules/z3/src/api/python/z3/lib -I /usr/local/opt/openssl/include -L /usr/local/opt/openssl/lib -Xlinker -rpath -Xlinker /usr/local/opt/openssl/lib -g -o time time.cpp -lz3 -pthread
time.cpp:32:9: error: must use 'class' tag to refer to type 'time' in this scope
typedef time ivy_class;
        ^
        class
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h:118:8: note: class 'time' is hidden by a non-type declaration of 'time' here
time_t time(time_t *);
       ^
time.cpp:193:27: error: must use 'class' tag to refer to type 'time' in this scope
    virtual bool generate(time& obj)=0;
                          ^
                          class
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h:118:8: note: class 'time' is hidden by a non-type declaration of 'time' here
time_t time(time_t *);
       ^
time.cpp:194:26: error: must use 'class' tag to refer to type 'time' in this scope
    virtual void execute(time& obj)=0;
                         ^
                         class
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h:118:8: note: class 'time' is hidden by a non-type declaration of 'time' here
time_t time(time_t *);
       ^
time.cpp:1291:14: error: must use 'class' tag to refer to type 'time' in this scope
    init_gen(time&);
             ^
             class
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h:118:8: note: class 'time' is hidden by a non-type declaration of 'time' here
time_t time(time_t *);
       ^
time.cpp:1292:19: error: must use 'class' tag to refer to type 'time' in this scope
    bool generate(time&);
                  ^
                  class
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h:118:8: note: class 'time' is hidden by a non-type declaration of 'time' here
time_t time(time_t *);
       ^
time.cpp:1293:18: error: must use 'class' tag to refer to type 'time' in this scope
    void execute(time&){}
                 ^
                 class
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h:118:8: note: class 'time' is hidden by a non-type declaration of 'time' here
time_t time(time_t *);
       ^
time.cpp:1295:20: error: must use 'class' tag to refer to type 'time' in this scope
init_gen::init_gen(time &obj){
                   ^
                   class
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h:118:8: note: class 'time' is hidden by a non-type declaration of 'time' here
time_t time(time_t *);
       ^
time.cpp:1301:25: error: must use 'class' tag to refer to type 'time' in this scope
bool init_gen::generate(time& obj) {
                        ^
                        class
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h:118:8: note: class 'time' is hidden by a non-type declaration of 'time' here
time_t time(time_t *);
       ^
8 errors generated.
(venv) ➜  accord_ivy git:(main) ✗

Failure of verification of liveness properties in examples

While attempting to verify the liveness-properties mentioned in the example-files provided in the repository, I found a couple of errors for some of the files, while most files checked OK.

  1. Hybrid Reliable Broadcast (Clock synchronization, Widder, Schmid)
    The following temporal property is being proved:

        liveness4.ivy: line 254: hrb.correctness
Traceback (most recent call last):
  File "/home/user/.local/bin/ivy_check", line 11, in <module>
    sys.exit(main())
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_check.py", line 793, in main
    check_module()
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_check.py", line 765, in check_module
    check_isolate()
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_check.py", line 579, in check_isolate
    check_temporals()
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_check.py", line 120, in check_temporals
    subgoals = pc.admit_proposition(prop,proof,subgoals)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_proof.py", line 107, in admit_proposition
    subgoals = self.apply_proof(subgoals,proof)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_proof.py", line 161, in apply_proof
    return self.tactic_tactic(decls,proof)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_proof.py", line 169, in tactic_tactic
    return tactic(self,decls,proof)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_l2s.py", line 559, in l2s_tactic
    assert len(named_binders['l2s_g']) == len(named_binders['_old_l2s_g'])
AssertionError
  1. [TLB] (https://github.com/kenmcmil/ivy/blob/master/examples/liveness/tlb.ivy)
The following temporal property is being proved:

        liveness4.ivy: line 903: tlb.nonstarvation
Traceback (most recent call last):
  File "/home/user/.local/bin/ivy_check", line 11, in <module>
    sys.exit(main())
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_check.py", line 793, in main
    check_module()
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_check.py", line 765, in check_module
    check_isolate()
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_check.py", line 579, in check_isolate
    check_temporals()
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_check.py", line 120, in check_temporals
    subgoals = pc.admit_proposition(prop,proof,subgoals)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_proof.py", line 107, in admit_proposition
    subgoals = self.apply_proof(subgoals,proof)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_proof.py", line 161, in apply_proof
    return self.tactic_tactic(decls,proof)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_proof.py", line 169, in tactic_tactic
    return tactic(self,decls,proof)
  File "/home/user/.local/lib/python2.7/site-packages/ivy/ivy_l2s.py", line 559, in l2s_tactic
    assert len(named_binders['l2s_g']) == len(named_binders['_old_l2s_g'])
AssertionError

  1. [Test Liveness 2] (https://raw.githubusercontent.com/microsoft/ivy/9f3c7ecc0b2383129fdd0953e10890d98d09a82d/test/test_liveness2.ivy)
...
 (internal) idle
Test_Liveness.ivy: line 294: ticket_protocol.invar68 ... FAIL
Test_Liveness.ivy: line 306: ticket_protocol.invar69 ... FAIL
...


    The following program assertions are treated as guarantees:
        in action idle when called from the environment,the environment:
            Test_Liveness.ivy: line 158: guarantee ... FAIL

error: failed checks: 3

  1. Test Liveness 1
Test_Liveness.ivy(201): error: token 'property': syntax error
  1. Leader Election
Test_Liveness.ivy(96): error: token 'call': syntax error
  1. Liveness 1
Isolate this:

    The following properties are to be checked:

    The following action implementations are present:
        Test_Liveness.ivy: line 10: implementation of a

    The following initializers are present:
        Test_Liveness.ivy: line 5: init[after1]

    Any assertions in initializers must be checked ... PASS

    The following temporal property is being proved:

        Test_Liveness.ivy: line 13: prop2

    The inductive invariant consists of the following conjectures:
        Test_Liveness.ivy: line 16: inv1

    The following action implementations are present:
        Test_Liveness.ivy: line 10: implementation of a

    The following initializers are present:
        Noneinit

    Initialization must establish the invariant
        Test_Liveness.ivy: line 16: inv1 ... PASS

    Any assertions in initializers must be checked ... PASS

    The following set of external actions must preserve the invariant:
        (internal) ext:a
            Test_Liveness.ivy: line 16: inv1 ... PASS
        (internal) idle
            Test_Liveness.ivy: line 16: inv1 ... PASS

    The following program assertions are treated as assumptions:
        in action idle when called from the environment:
            Test_Liveness.ivy: line 13: assumption
            Test_Liveness.ivy: line 13: assumption
            Test_Liveness.ivy: line 13: assumption
        in action a when called from the environment:
            Test_Liveness.ivy: line 13: assumption
            Test_Liveness.ivy: line 13: assumption
            Test_Liveness.ivy: line 13: assumption
            Test_Liveness.ivy: line 13: assumption

    The following program assertions are treated as guarantees:
        in action idle when called from the environment,the environment:
            Test_Liveness.ivy: line 13: guarantee ... FAIL

error: failed checks: 1

Verbose Unbounded Model Checking

We're using IVy to explore some of the properties of interpolation and unbounded model checking. It seems that using pedantic=true on the command line doesn't add information to the model checking trace. Is there a way to see the interpolants that are created during unbounded model checking in the returned trace (like some sort of verbose mode)?

Failure in compiling to cpp, ivy-script that checks correctly

I've written the following example, for the purpose of examining the following question:

Is there a way to copy the entries of a relation that's defined within the local-scope of an object, into another relation that is in the global-scope of the application? 

The code that I have written checks correctly, by using ivy_check. However, when I attempt to extract an executable, using ivy_to_cpp, I obtain the following compilation error:

$ ivy_to_cpp test_copy_relations.ivy 
test_copy_relations.ivy: line 45: error: cannot compile method_1.localREL(arg@@arg0,arg@@arg1)

The code-script for test_copy_relations.ivy is as follows:

#lang ivy1.7


type setA
type setB

interpret setA -> bv[8]
interpret setB -> bv[4]

relation globalREL(A:setA,B:setB)
relation glocalREL(A:setA,B:setB) # Captures the relation-entry updates(fresh inserts, NO DELETES) that happen within each local-method. 

after init{
	globalREL(A,B):=false;
	glocalREL(A,B):=false;
}

type method 


object method_1 ={
	variant this of  method

	relation localREL(LA:setA, LB:setB)

	after init{
		localREL(LA, LB) := false;
	}

	action updateLocalRel(a:setA, b:setB) = {

		# randomly generate a,b


		# update the relation. 
		localREL(a,b) := true;

		#update glocal
		glocalREL(a,b) := true;
	}

	action commitToGlobal = {

		# We are copying values from one relation to another. 
		globalREL(X,Y) := localREL(X,Y);
	}

	after commitToGlobal{
		assert localREL(X,Y) -> globalREL(X,Y);
		localREL(X,Y) := false;
	}


}



object method_2 ={
	variant this of  method

	relation localREL(LA:setA, LB:setB)

	after init{
		localREL(LA, LB) := false;
	}

	action deleteLocalRel(a:setA, b:setB) = {


		if *{
				# update the relation with new entry with randomly initialized values. 
				localREL(a,b) := true;
				glocalREL(a,b) := true;

			} else{

				# delete a pre-existing element and then update with its successor elemental parts. 

				if some x:setA, y:setB. (globalREL(x,y)=true){

						localREL(x,y) := false;
						localREL(x+1, y+1) := true;
						glocalREL(x+1,y+1) := true;
				};

			};

			
		
	}

	action commitToGlobal = {

		# ensure forall X,Y. localREL(X,Y) -> globalREL(X,Y)
		# We are copying values from one relation to another. 
		globalREL(X,Y) := localREL(X,Y);
	}

	after commitToGlobal{
		assert localREL(X,Y) -> globalREL(X,Y);
		localREL(X,Y) := false;
	}



}




# invariant [inv_one] forall X,Y. globalREL(X,Y) -> glocalREL(X,Y) 			# This should pass. 



export method_2.deleteLocalRel
export method_2.commitToGlobal

export method_1.updateLocalRel
export method_1.commitToGlobal



attribute radix=16       # print in hex


There seems to be little documentation available on the IvY website, that helps to use all the features that exist in the Ivy toolchain. It would really help, if we have some documentation. I can volunteer to help develop this, with some guidance from the team. Thank you for your help.

Requesting Pointers on Unbounded Model Checker

We are using the unbounded model checker feature of IVy to verify livelock issues in network-on-chip. We'd like to give a description of the ABC system, specifically what part(s) of ABC IVy's unbounded model checker uses.

Additionally, if anyone can provide references about IVy's unbounded model checker, we would greatly appreciate your insights.

I couldn't make the Socrates example work

I was reading http://microsoft.github.io/ivy/proving.html and I tried to put together the proof that the Socrates is mortal by copying and pasting the code from the tutorial.

#lang ivy1.7    
                
type t        
              
relation man(X:t)              
relation mortal(X:t)        
                                     
axiom [mortality_of_man] {        
    property [prem] man(X)        
    #---------------         
    property mortal(X)                                
}                                                 
                                                  
individual socrates : t    
                           
axiom [soc_man] man(socrates)    
                                 
property mortal(socrates)    
proof {                      
    apply mortality_of_man with prem = soc_man    
}  

When I run ivy_check diagnose=true proof.ivy I get proof.ivy: line 20: error: unknown symbol: prem where Line 20 is apply mortality_of_man with prem = soc_man. It seems odd since I had already defined the property prem earlier.

I'm wondering how I can make this work. Thanks!

ivy_to_c++: "Missing members in `tuple_test`" errors

I'm writing an Ivy specification which attempted to partially initialise a relation in an after init block. Here's a distillation of the intention:

#lang ivy1.8

type range_t = {0..max}

process proc = {
    specification {
        relation rel(X: range_t, Y: range_t, Z: range_t)

        after init {
            rel(X,0,Z) := false;
        }
    }
}

(apologies if this code sample is opaque in its intent.)

When compiling with ivyc target=test tuple_test.ivy, I'd expect either an Ivy compilation error, or, a successful ivy -> g++ pipeline pass. However, we die inside some generated code:

➜  sandbox git:(main) ✗ ivyc target=test tuple_test.ivy
g++ -Wno-parentheses-equality  -std=c++11  -I /Library/Python/2.7/site-packages/ms_ivy-1.8.4-py2.7.egg/ivy/include -L /Library/Python/2.7/site-packages/ms_ivy-1.8.4-py2.7.egg/ivy/lib -Xlinker -rpath -Xlinker /Library/Python/2.7/site-packages/ms_ivy-1.8.4-py2.7.egg/ivy/lib -I /usr/local/opt/openssl/include -L /usr/local/opt/openssl/lib -Xlinker -rpath -Xlinker /usr/local/opt/openssl/lib -g -o tuple_test tuple_test.cpp -lz3 -pthread
tuple_test.cpp:1216:16: error: unknown type name '__tup__int__int'; did you mean '__tup__int__int__int'?
    hash_thunk<__tup__int__int,bool> __tmp0;
               ^~~~~~~~~~~~~~~
               __tup__int__int__int
./tuple_test.h:637:8: note: '__tup__int__int__int' declared here
struct __tup__int__int__int {
       ^
tuple_test.cpp:1219:32: error: no member named '__tup__int__int' in 'tuple_test'
            __tmp0[tuple_test::__tup__int__int(X,Z)] = false;
                   ~~~~~~~~~~~~^
tuple_test.cpp:1224:116: error: no member named '__tup__int__int' in 'tuple_test'
            proc__rel[tuple_test::__tup__int__int__int(X,( 0 < 0 ? 0 : max < 0 ? max : 0),Z)] = __tmp0[tuple_test::__tup__int__int(X,Z)];
                                                                                                       ~~~~~~~~~~~~^
3 errors generated.

I can make it successfully pass by:

  • Changing the set assignment to rel(X,Y,Z) := false;;
  • Removing a column from the tuple (i.e. rel now consumes only an X and a Y);
  • Giving a concrete upper bound on the range_t type;
  • Removing target=test.

Thanks!
Nathan

Initialization of modules with vector-fields causes compilation error

#lang ivy1.7

include collections

type request_type = {req1, req2, req3}
type response_type = {res1, res2, res3, res4}

type messageID
type message

module messageIE(t) = {

	variant this of message = struct{
	 id : messageID,
	 payload : vector[t]
	}

}


instance responseIE : messageIE(response_type)
instance requestIE : messageIE(request_type)

interpret messageID -> int
interpret request_type -> bv[16]
interpret response_type -> bv[8]

I'm trying to generate a message with the above complex structure. The payload is supposed to be a vector of the type that parametrizes the module.

The responseIE and requestIE are desired to be variants of type message, with the difference being that they carry payloads of different types.

I get the following error on compilation with ivy-compiler.

 ivyc target=test messageIE.ivy 
g++  -I /home/user/.local/lib/python2.7/site-packages/ivy/include -L /home/user/.local/lib/python2.7/site-packages/ivy/lib -Wl,-rpath=/home/user/.local/lib/python2.7/site-packages/ivy/lib -g -o messageIE messageIE.cpp -lz3 -pthread
In file included from messageIE.cpp:1:0:
messageIE.h: In instantiation of ‘size_t hash_space::hash<T>::operator()(const T&) const [with T = messageIE::response_type; size_t = long unsigned int]’:
messageIE.h:111:25:   required from ‘size_t hash_space::hash<std::vector<T> >::operator()(const std::vector<T>&) const [with T = messageIE::response_type; size_t = long unsigned int]’
messageIE.h:626:101:   required from here
messageIE.h:44:22: error: request for member ‘__hash’ in ‘s’, which is of non-class type ‘const messageIE::response_type’
             return s.__hash();
                    ~~^~~~~~
messageIE.h: In instantiation of ‘size_t hash_space::hash<T>::operator()(const T&) const [with T = messageIE::request_type; size_t = long unsigned int]’:
messageIE.h:111:25:   required from ‘size_t hash_space::hash<std::vector<T> >::operator()(const std::vector<T>&) const [with T = messageIE::request_type; size_t = long unsigned int]’
messageIE.h:629:100:   required from here
messageIE.h:44:22: error: request for member ‘__hash’ in ‘s’, which is of non-class type ‘const messageIE::request_type’

Should properties and invariants of sub-isolates be hidden by default?

Consider this example:

#lang ivy1.7
relation r1
isolate a = {
    axiom r1
}
isolate b = {
    invariant true
} with this

and the output of ivy_show isolate=b:

relation r1
axiom [a.axiom1] r1
conjecture [b.invar2] true

Should axiom r1 appear here? Or would it be better to require an explicit with a to bring in this property? Because of the current behavior, it is sometimes awkward to isolate properties and invariants that have quantifier alternations. For invariants, it seems one can use explicit as a workaround, but it does not work for properties.

Ivy script code-extraction fails in both `ivy1.7 and ivy1.8` but `Checks OK` and generates testing agent

For the below script, we obtain the following error when extracting code using the comand

$ ivyc target=test build=true isolate=iso_R test_me.ivy
...
test_me.ivy: line 76: error: Call out to righty.spec.flip may have visible effect on righty.spec.ball
test_me.ivy: line 76: referenced here
test_me.ivy: line 17: referenced here

However, when compiling it to generate a randomized test-agent, it compiles correctly and produces an executable:

$ ivyc target=test build=true isolate=iso_right test_me.ivy 
g++  -I /home/user/.local/lib/python2.7/site-packages/ivy/include -L /home/user/.local/lib/python2.7/site-packages/ivy/lib -Wl,-rpath=/home/user/.local/lib/python2.7/site-packages/ivy/lib -g -o test_me test_me.cpp -lz3 -pthread
...
$ ./test_me true
...
< righty.impl.debug.send(74,msg_ping)
> righty.impl.trans.recv(1,msg_ping)
< righty.impl.debug.recv(1,msg_ping)
> righty.impl.trans.recv(2,msg_pong)
< righty.impl.debug.recv(2,msg_pong)
> righty.impl.trans.recv(2,msg_pong)
< righty.impl.debug.recv(2,msg_pong)
> righty.spec.hit(61)
< righty.impl.trans.send(61,61,msg_pong)
< righty.impl.debug.send(61,msg_pong)
> righty.spec.hit(87)
< righty.impl.trans.send(87,87,msg_ping)
< righty.impl.debug.send(87,msg_ping)
> righty.impl.trans.recv(1,msg_ping)
< righty.impl.debug.recv(1,msg_ping)
> righty.spec.hit(151)
< righty.impl.trans.send(151,151,msg_ping)
< righty.impl.debug.send(151,msg_ping)
> righty.spec.hit(128)
< righty.impl.trans.send(128,128,msg_pong)
< righty.impl.debug.send(128,msg_pong)
> righty.spec.hit(219)
< righty.impl.trans.send(219,219,msg_ping)
< righty.impl.debug.send(219,msg_ping)
> righty.spec.hit(86)
< righty.impl.trans.send(86,86,msg_pong)
< righty.impl.debug.send(86,msg_pong)
test_completed

The script is provided below:

#lang ivy1.7

include udp

type message_t = {msg_ping, msg_pong}


# ------ DEBUGGING 

module debugmod(node, msgtype, udp) = {
    action send(dst:node,m:msgtype)
    action recv(dts:node,m:msgtype)
    before udp.send(src:node,dst:node,msg:msgtype) {
        call send(dst,msg)
    }
    before udp.recv(dst:node,msg:msgtype) {
        call recv(dst,msg)
    }
}

module nodule = {
    type this


    implementation{

        interpret this -> bv[8]

    }

    isolate iso_node = this
    
}

instance node : nodule

module player = {

    object spec={

        parameter ball : bool

        action hit(me:node)
        
        #after init(me:node) { ball := true; }    

        action flip(me:node)={
            ball := ~ball;
        }    

    }


    object impl = {
        instance trans: udp_simple2(node, message_t)
        instance debug : debugmod(node, message_t, trans)

        implement spec.hit(me:node) {
            if (spec.ball ) {

                call trans.send(me, me, msg_ping); # #call intf.ping; # call transferred to reception.
                call spec.flip(me);
            }else{

                call trans.send(me, me, msg_pong); # #call intf.ping; # call transferred to reception.
                call spec.flip(me);
            }
        }


        implement trans.recv(dst:node, v:message_t){

            if (v = msg_ping  &  ~spec.ball ){
                
                call spec.flip(dst);

            }else if (v = msg_pong  &  spec.ball ){
                
                call spec.flip(dst);

            };

        }
    }



}



instance righty : player


export righty.spec.hit

import righty.impl.debug.send
import righty.impl.debug.recv

isolate iso_right = righty with node, message_t

extract iso_R(me:node) = righty(me), node, message_t


Incorrect "wrong number of input parameters" error

Hello,

I was checking out the toy_consensus.ivy from the examples: https://github.com/kenmcmil/ivy/blob/master/doc/examples/toy_consensus.ivy

When trying to compile the example to cpp I get the following error:

$ ivy_to_cpp target=test isolate=iso_impl toy_consensus.ivy 
toy_consensus.ivy: line 217: error: wrong number of input parameters

which references a call to shim.bcast call shim.bcast(self,msg);. Which is defined as follows:

    action bcast(src:node,m:msg)

Which seems to take two input parameters? When changing the call from call shim.bcast(self,msg); to either call shim.bcast(self); or call shim.bcast(self,msg,msg); I get the following errors:

toy_consensus.ivy: line 217: error: wrong number of input parameters (got 1, expecting 2)
toy_consensus.ivy: line 217: error: wrong number of input parameters (got 3, expecting 2)

When commenting out the call in line 217, ivy_to_cpp successfully compiles to cpp. Even though there are other calls to shim.bcast in the file with two arguments, e.g. in lines 233 call shim.bcast(self,reply);

TypeError: ivy_load_file() takes exactly 1 argument (2 given)

I followed the instruction in https://cs.stanford.edu/~padon/ivy/index.html#downloading.

When I run :
python ../../ivy/ivy2.py leader_election_ring.ivy

I get a type error:

TypeError                                 Traceback (most recent call last)
<ipython-input-1-5502424a515f> in <module>()
     18 ctx.__enter__()
     19 ivy_widget = AnalysisSessionWidget()
---> 20 session = AnalysisSession('leader_election_ring.ivy', ivy_widget)
     21 set_context(session)
     22 ivy_widget.transition_view.conjectures = session.analysis_state.ivy_interp.conjs[:]

/home/lsq/ivy/ivy/proof.pyc in __init__(self, filename, widget)
     62         self.filename = filename
     63         try:
---> 64             m = IvyModel(filename)
     65         except IvyError as e:
     66             raise e

/home/lsq/ivy/ivy/proof.pyc in __init__(self, filename)
     24         ag = ivy_new()
     25         try:
---> 26             ivy_load_file(open(filename), ag)
     27         except IvyError as e:
     28             e.filename = filename

TypeError: ivy_load_file() takes exactly 1 argument (2 given)

Request for assistance in debugging: Verifying specifications for system with network-messaging

The script that I'm trying to verify, is a connection-management protocol used by multiple-clients with one common server. The protocols consists of a two-way handshake. Connction-Request Message is sent by a client that is not connected to the Server. The Server may non-deterministically choose to either Accept the request or Reject it, by sending as a response the Connection-Accept message or a Connection-Reject message. The ivy-script for the same can be obtained here.

I'm having issues in debugging the type of failures indicated, as the structure of the network library, seems to be quite different from that of previous versions of IVy( < v1.8).

I have obtained the following error when checking the code using : ivy_check trace=true filename.ivy

    Any assertions in initializers must be checked ... PASS

    The following set of external actions must preserve the invariant:
        (internal) ext:client.hit
            connection_mgmt.ivy: line 87: client.invar300 ... PASS
        (internal) ext:client.server.sock.recv
            connection_mgmt.ivy: line 87: client.invar300 ... FAIL
searching for a small model... done
[
    index.succ(1,2) = true
    index.succ(0,1) = true
    index.succ(1,1) = false
    index.succ(1,0) = false
    index.succ(0,0) = false
    index.succ(0,2) = false
    index.succ(2,1) = false
    index.succ(2,0) = false
    index.succ(2,2) = false
    0:index = 0
    client_id.max = 0
    client.connected_clients(0) = true
    @X = 0
    client.sock.id(0) = 0
    client_id.succ(0,0) = false
    client.connectionRequested(0) = true
    client.pkt.kind(0) = conn_req
    client.pkt.kind(1) = conn_rej
    client.pkt.src_client(0) = 0
    client.pkt.src_client(1) = 0
    0:client_id = 0
    0:client_id < 0 = false
    1:index < 2 = true
    0:index < 1 = true
    0:index < 2 = true
    1:index < 1 = false
    1:index < 0 = false
    0:index < 0 = false
    2:index < 1 = false
    2:index < 0 = false
    2:index < 2 = false
    client.server.sock.id = 0
    client.net.tail(0,0) = 0
    client.connectionRejected(0) = false
    client.net.head(0,0) = 1
    client.connectionAccepted(0) = true
    client.net.sent(0,0,0,0) = true
    client.net.sent(0,0,2,0) = true
    client.net.sent(0,0,1,0) = false
    client.net.sent(0,0,1,1) = false
    client.net.sent(0,0,0,1) = false
    client.net.sent(0,0,2,1) = false
]
call client.server.sock.recv

{
    [
        fml:src = 0
        fml:msg = 0
    ]
/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 117: 
    assume client.net.tail(X,Y) <= client.net.head(X,Y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 118: 
    assume client.net.sent(X,Y,T,M1) & client.net.sent(X,Y,T,M2) -> M1 = M2

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 117: 
    assume client.net.tail(X,Y) <= client.net.head(X,Y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 118: 
    assume client.net.sent(X,Y,T,M1) & client.net.sent(X,Y,T,M2) -> M1 = M2

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 112: 
    assume client.net.tail(fml:src,client.server.sock.id) < client.net.head(fml:src,client.server.sock.id)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 113: 
    assume client.net.sent(fml:src,client.server.sock.id,client.net.tail(fml:src,client.server.sock.id),fml:msg)

    [
        fml:y = 1
    ]
/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 114: 
    call client.net.tail(fml:src,client.server.sock.id) := index.next(client.net.tail(fml:src,client.server.sock.id))
    {
        [
            fml:x = 0
        ]
/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/order.ivy: line 38: 
        assume fml:x < fml:y & (fml:x < Y -> fml:y <= Y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/order.ivy: line 39: 
        assume index.succ(fml:x,fml:y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 117: 
        assume client.net.tail(X,Y) <= client.net.head(X,Y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 118: 
        assume client.net.sent(X,Y,T,M1) & client.net.sent(X,Y,T,M2) -> M1 = M2

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 117: 
        assume client.net.tail(X,Y) <= client.net.head(X,Y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 118: 
        assume client.net.sent(X,Y,T,M1) & client.net.sent(X,Y,T,M2) -> M1 = M2

    }

    [
        client.net.tail(0,0) = 1
    ]
connection_mgmt.ivy: line 122: 
    call client.server.recvin(fml:msg)
    {
        [
            fml:v = 0
        ]
        return

    }

connection_mgmt.ivy: line 138: 
    call client.connection_reject(client.pkt.src_client(fml:msg))
    {
        [
            fml:agent = 0
        ]
connection_mgmt.ivy: line 58: 
        assert ~client.connected_clients(fml:agent)

connection_mgmt.ivy: line 59: 
        assert client.connectionRequested(fml:agent) & ~client.connectionAccepted(fml:agent) & ~client.connectionRejected(fml:agent)

connection_mgmt.ivy: line 60: 
        client.connectionRejected(fml:agent) := true

        [
            client.connectionRejected(0) = true
        ]
    }

    [
        loc:msg = 0
    ]
connection_mgmt.ivy: line 140: 
    client.pkt.kind(loc:msg) := conn_rej

    [
        loc:msg = 1
    ]
connection_mgmt.ivy: line 141: 
    call client.server.sock.send(client.sock.id(client.pkt.src_client(fml:msg)), loc:msg)
    {
        [
            fml:dst = 0
            fml:msg_a = 1
        ]
/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 108: 
        client.net.sent(client.server.sock.id,fml:dst,client.net.head(client.server.sock.id,fml:dst),fml:msg) := true

        [
            client.net.sent(0,0,1,1) = true
            fml:y = 2
        ]
/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 109: 
        call client.net.head(client.server.sock.id,fml:dst) := index.next(client.net.head(client.server.sock.id,fml:dst))
        {
            [
                fml:x = 1
            ]
/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/order.ivy: line 38: 
            assume fml:x < fml:y & (fml:x < Y -> fml:y <= Y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/order.ivy: line 39: 
            assume index.succ(fml:x,fml:y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 117: 
            assume client.net.tail(X,Y) <= client.net.head(X,Y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 118: 
            assume client.net.sent(X,Y,T,M1) & client.net.sent(X,Y,T,M2) -> M1 = M2

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 117: 
            assume client.net.tail(X,Y) <= client.net.head(X,Y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 118: 
            assume client.net.sent(X,Y,T,M1) & client.net.sent(X,Y,T,M2) -> M1 = M2

        }

        [
            client.net.head(0,0) = 2
            fml:y = 1
        ]
/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 117: 
        assume client.net.tail(X,Y) <= client.net.head(X,Y)

/usr/local/lib/python2.7/dist-packages/ivy/include/1.8/network.ivy: line 118: 
        assume client.net.sent(X,Y,T,M1) & client.net.sent(X,Y,T,M2) -> M1 = M2

    }

    [
        loc:msg = 0
    ]
}

encode_ite in unbounded model checking

We're using unbounded model checking, and we were hoping to check a model that involves multiplication. The encode_times function in ivy_mc.py calls encode_ite, a function which appears not to exist. We were hoping you have pointers on this. Thanks in advance!

Ivy doesn't expand a nested macro

#lang ivy1.7

type mytype

relation always_true(MYTYPE:mytype)
definition always_true(MYTYPE) = true

relation always_true_macro(MYTYPE:mytype)
definition always_true_macro(mytype_:mytype) = true

relation nested_always_true(MYTYPE:mytype)
definition nested_always_true(mytype_:mytype) = always_true(mytype_)

relation nested_always_true_macro(MYTYPE:mytype)
definition nested_always_true_macro(mytype_:mytype) = always_true_macro(mytype_)

invariant forall X. always_true(X) # Pass
invariant forall X. always_true_macro(X) # Pass
invariant forall X. nested_always_true(X) # Pass
invariant forall X. nested_always_true_macro(X) # Fail

Ivy seems to fail to expand always_true_macro inside nested_always_true_macro and shows the following CTI:

searching for a small model... done
[
    @X = 0
    always_true(0) = true
    always_true_macro(0) = false
    nested_always_true_macro(0) = false
]

2to3 branch GUI problem

It looks like exploring nodes in the GUI just throws an error.

For example, when reviewing a model that has problems with diagnose=true, and clicking on any node yields:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.8/tkinter/__init__.py", line 720, in getboolean
    return self.tk.getboolean(s)
_tkinter.TclError: expected boolean value but got "??"

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.8/tkinter/__init__.py", line 1882, in __call__
    args = self.subst(*args)
  File "/usr/lib/python3.8/tkinter/__init__.py", line 1569, in _substitute
    try: e.focus = getboolean(f)
  File "/usr/lib/python3.8/tkinter/__init__.py", line 722, in getboolean
    raise ValueError("invalid literal for getboolean()")
ValueError: invalid literal for getboolean()

I got this error with the model from the tutorial, and clicking on any client:

#lang ivy1.7

type client
type server

relation link(X:client, Y:server)
relation semaphore(X:server)

after init {
    semaphore(W) := true;
    link(X,Y) := false
}

action connect(x:client,y:server) = {
  require semaphore(y);
  link(x,y) := true;
  semaphore(y) := false
}

action disconnect(x:client,y:server) = {
  require link(x,y);
  link(x,y) := false;
  semaphore(y) := true
}

private {
#invariant [server_tracks] link(C, S) -> semaphore(S) = false
}

export connect
export disconnect

invariant [no_dupe] link(C1, S) & link(C2, S) -> C1 = C2

Code Extraction - for Asymmetric Protocols

Compiled using IVy 1.7

I'm attempting to re-implement the PING-PONG tutorial, with the networking module, so that the actions ping and pong may be translated into messages sent over the network. The correctness of the ping-pong protocol, should be checked for correctness, in consideration of the network dynamics.

source:Specification Tutorial

I have attempted to implement this, using the following codebase. However, it generates the following error:

$ ivy_check ping_pong_trans_v2.ivy 

Isolate abstract_protocol:
error: The verification condition is not in the fragment FAU.

The following terms may generate an infinite sequence of instantiations:
  
ping_pong_trans_v2.ivy: side(neighbor(N_b))
    (position 0 is a function from node to node)


Is there an alternative/easier representation that's possible within IVy? Or is the code-extraction within IVy designed only for symmetric protocols?

#lang ivy1.7

include order
include timeout 
include udp

isolate id = {
    type this

    specification{
        instantiate totally_ordered(this)
    }

    implementation{
        interpret this -> bv[32]
    }
}


object node = {
    type this

    object leftnode = {
        variant this of node 
    }

    object rightnode = {
        variant this of node
    }
    
}

interpret node ->bv[1]

parameter pid(N:node) : id
axiom [pid_injective] pid(N) = pid(M) -> N = M


# Captures the GLobal-Network: Mapping between LeftPlayers and RightPlayers
parameter neighbor(N:node) : node
axiom [neighbor_one_to_one] neighbor(N) = neighbor(M) <-> N = M
#axiom [neighbor_no_self_loop] neighbor(N) ~= N


# Captures the global-notion of SIDE
parameter side(N:node) : bool
axiom [opponents_sides] side(N) & side(neighbor(N)) = 0




type packet={ping, pong}
type players_t = {left, right}




isolate abstract_protocol ={
    

    # -------------------------------------
    # INTERFACE: 
    # -------------------------------------
    
    # HOW TO REPRESENT STATE - OF WHICH AGENT's side it is. 
    # function side(n:node, m:node) : players_t


    action send(n:node, p:packet)           # node-n sends pid-p to the next-node in the ring.


    action ping
    action pong

    # -------------------------------------
    # SPECIFICATION: 
    # -------------------------------------
    specification{
        relation sent(P:packet, N:node)    

        after init{
            sent(P,N) := false;         # Empty Channel.


            # How should this be initialized, such that the left-agents can initialize their pings.
            # Is this already captured by the property: `opponents_sides` ??
            #side(N:node.leftnode) := true;      # LEFT-NODES
            #side(N:node.rightnode) := false;    # RIGHT-NODES
        }


        after send{

            sent(p, neighbor(n)) := true;
        }


    }




} with node, id, pid_injective, neighbor_one_to_one, opponents_sides #, neighbor_no_self_loop


isolate refined_protocol_left = {
    
    # -------------------------------------
    # INTERFACE: 
    # -------------------------------------
    # THere's no interface. At each timer-expiry, the LEFT-NODE sends the PING-message if it has its turn set.

    individual ball : bool
    after init {
        ball := true
    } 

    # -------------------------------------
    # IMPLEMENTATION:  SEQUENTIAL REACTIVE PROGRAM
    # -------------------------------------
    implementation{

        #instantiate net: udp_simple2(node.leftnode, packet)      # Networking Service: `address_type`: node, `packet_type`: packet
        instantiate net: udp_simple2(node, packet)      
        
        # PARAMETERIZED collection of instances of the `timeout_sec` service:
        #instantiate timer(N:node.leftnode) : timeout_sec     # There is ONE instance of `timeout_sec` for each node 
        instantiate timer(N:node) : timeout_sec     # There is ONE instance of `timeout_sec` for each node 


        implement timer.timeout(self:node){

            if ball{
                call abstract_protocol.send(self, ping);   

                # PING

                # ACTUAL: 
                call net.send(self, neighbor(self), ping);
                ball := false;            
            };

        }

        #implement net.recv(self:node.leftnode, v:packet){
        implement net.recv(self:node, v:packet){

            if v=pong{
                # GHOST: Having NO EFFECT in extracted implementation. 
                call abstract_protocol.pong;

            };

        }
    }


    private{        # REFINEMENT RELATION


        # [Prop 3] The state of the implementation should be mapped to corresponding states of the abstract-protocol state. 
        invariant net.sent(V,N) -> abstract_protocol.sent(V,N)


        # TODO: How do we represent this relationship between the ConcreteModel and the AbstractModel? 
        #invariant ball -> side(self) = true
    }

#} with node.leftnode, id, pid_injective, neighbor_one_to_one, neighbor_no_self_loop
} with abstract_protocol, node, id, pid_injective, neighbor_one_to_one, opponents_sides #, neighbor_no_self_loop

#extract leftcode(n:node.leftnode) = refined_protocol(n), pid(n), node.leftnode, id
extract leftcode(n:node) = refined_protocol_left(n), pid(n), node, id





isolate refined_protocol_right = {
    
    # -------------------------------------
    # INTERFACE: 
    # -------------------------------------
    # THere's no interface. At each timer-expiry, the UE sends the PING-message if it has its turn set. 

    individual ball : bool
    after init {
        ball := false;
    } 

    # -------------------------------------
    # IMPLEMENTATION:  SEQUENTIAL REACTIVE PROGRAM
    # -------------------------------------
    implementation{

        #instantiate net: udp_simple2(node.rightnode, packet)      # Networking Service: `address_type`: node, `packet_type`: id
        instantiate net: udp_simple2(node, packet)     
        
        # PARAMETERIZED collection of instances of the `timeout_sec` service:
        #instantiate timer(N:node.rightnode) : timeout_sec     # There is ONE instance of `timeout_sec` for each node 
        instantiate timer(N:node) : timeout_sec


        #implement timer.timeout(self:node.rightnode){
        implement timer.timeout(self:node){
            

            if ball{
                # GHOST: Having NO EFFECT in extracted implementation. 
                call abstract_protocol.send(self, pong);   


                # ACTUAL: 
                call net.send(self, neighbor(self), pong);
                ball := false;            
            };


        }

        #implement net.recv(self:node.rightnode, v:packet){
        implement net.recv(self:node, v:packet){

            if v=ping{
                # GHOST: Having NO EFFECT in extracted implementation. 
                call abstract_protocol.ping;

            };

        }
    }


    private{        # REFINEMENT RELATION


        # [Prop 3] The state of the implementation should be mapped to corresponding states of the abstract-protocol state. 
        invariant net.sent(V,N) -> abstract_protocol.sent(V,N)

        # TODO: How do we represent this relationship between the ConcreteModel and the AbstractModel? 
        #invariant ball -> (abstract_protocol.side = left)
        #invariant ball -> side(self) = true
    }

#} with node.rightnode, id, pid_injective, neighbor_one_to_one, neighbor_no_self_loop
} with abstract_protocol, node, id, pid_injective, neighbor_one_to_one, opponents_sides #, neighbor_no_self_loop

#extract rightcode(n:node.rightnode) = refined_protocol(n), pid(n), node.rightnode, id
extract rightcode(n:node) = refined_protocol_right(n), pid(n), node, id


curiosity lowering boolean variable in definition

Here is a reduced testcase that fails ivy_check:

#lang ivy1.7
object foobar = {
    relation foo(BAR:bool)
    definition foo(BAR:bool) = true
    invariant forall BAR. foo(BAR)
} 

ivy_check crashes with an assert False after emitting bad fmla: Var('BAR', BooleanSort()) inside formula_to_z3_int. It doesn't seem to like the BAR variable inside the definition of foo.

This version (with bar:bool in the definition) works:

#lang ivy1.7
object foobar = {
    relation foo(BAR:bool)
    definition foo(bar:bool) = true
    invariant forall BAR. foo(BAR)
} 

As does this version (with a 2-element non-bool type):

#lang ivy1.7
type booly = {truey, falsey}
object foobar = {
    relation foo(BAR:booly)
    definition foo(BAR:booly) = true
    invariant forall BAR. foo(BAR)
} 

Subclass actions don't typecheck until ivytocpp

Hi Ken,

I have the following message type with a bunch of subclasses thereof:

 16     class msg_t = {
 17         action handle(cid: node, ^msg:msg_t)
 18     }
...
 38     subclass commit_msg_t of msg_t = {
 39         field src: node
 40         field txn: txn_t
 41         field execute_at: timestamp
 42
 43         field deps: vector[txn_t]
 44
 45         action handle(self: node, ^msg:commit_msg_t)
 46     }
 47
 48     subclass accept_msg_t of msg_t = {
 49         field src: node
 50         field txn: txn_t
 51         field execute_at: timestamp
 52
 53         field deps: vector[txn_t]
 54
 55         action handle(self: node, ^msg:commit_msg_t)
 56     }

Note the copy and paste error on line 55: clearly, that should be accept_msg_t. However, ivy appears to accept this, only to die while trying to compile the extracted C++.

(venv) ➜  accord-ivy git:(main) ✗ make
cd src; ivyc target=test protocol.ivy
g++ -Wno-parentheses-equality  -std=c++11  -I /Users/ntaylor/code/ivy/ivy/include -L /Users/ntaylor/code/ivy/ivy/lib -Xlinker -rpath -Xlinker /Users/ntaylor/code/ivy/ivy/lib -I /Users/ntaylor/code/ivy/submodules/z3/src/api/python/z3/include -L /Users/ntaylor/code/ivy/submodules/z3/src/api/python/z3/lib -Xlinker -rpath -Xlinker /Users/ntaylor/code/ivy/submodules/z3/src/api/python/z3/lib -I /usr/local/opt/openssl/include -L /usr/local/opt/openssl/lib -Xlinker -rpath -Xlinker /usr/local/opt/openssl/lib -g -o protocol protocol.cpp -lz3 -pthread
protocol.cpp:3382:47: error: no viable conversion from 'protocol::accept_msg_t' to 'const protocol::commit_msg_t'
                accept_msg_t__handle(prm__V0, self__COLON__accept_msg_t);
                                              ^~~~~~~~~~~~~~~~~~~~~~~~~
./protocol.h:829:12: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'protocol::accept_msg_t' to 'const protocol::commit_msg_t &' for 1st argument
    struct commit_msg_t {
           ^
./protocol.h:829:12: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'protocol::accept_msg_t' to 'protocol::commit_msg_t &&' for 1st argument
protocol.cpp:3090:67: note: passing argument to parameter 'msg' here
void protocol::accept_msg_t__handle(int self, const commit_msg_t& msg){
                                                                  ^
1 error generated.
make: *** [srcprotocol] Error 1
(venv) ➜  accord-ivy git:(main) ✗

Seems like either the parsing or extraction code should complain; or, C++ codegen should compile. (I suspect the former is the thing we want; not sure exactly what the execution semantics of the latter would be.)

Error in compiling `total order properties` code in examples with ivy1.7

While executing some of the documented examples in Ivy Leader Election[http://microsoft.github.io/ivy/examples/leader.html], with ivy1.7, I found some errors with ivy_check.

#lang ivy1.7

module total_order_properties(t) = {
    property [transitivity] X:t < Y & Y < Z -> X < Z
    property [antisymmetry] ~(X:t < Y & Y < X)
    property [totality] X:t < Y | X = Y | Y < X
}

isolate id = {
    type this

    specification {
        instantiate total_order_properties(this)
    }
}

Compiling the same with ivy_check generates 3 errors.

$ ivy_check id.ivy 

Isolate id:

    The following properties are to be checked:
        id.ivy: line 4: id.transitivity ... FAIL
        id.ivy: line 5: id.antisymmetry ... FAIL
        id.ivy: line 6: id.totality ... FAIL

    The following temporal property is being proved:

        id.ivy: line 4: id.transitivity

    The following temporal property is being proved:

        id.ivy: line 5: id.antisymmetry

    The following temporal property is being proved:

        id.ivy: line 6: id.totality

Isolate this:

    The following properties are assumed as axioms:
        id.ivy: line 4: id.transitivity
        id.ivy: line 5: id.antisymmetry
        id.ivy: line 6: id.totality

error: failed checks: 3

Additionally, when generating a trace of the same, we obtain the following:

$ ivy_check trace=true id.ivy 

Isolate id:

    The following properties are to be checked:
        id.ivy: line 4: id.transitivity ... FAIL
searching for a small model... done
[
    @Y = 0
    1:id < 0 = true
    0:id < 1 = true
    0:id < 0 = true
    1:id < 1 = false
    @Z = 1
    @X = 1
]

Invalid cpp code on windows when generating repl

There seems to be an issue when generating code for windows.
While compiling there is a undefined function call to "TimersThreadFunction".
Looking through the code I found a function defined TimerThreadFunction, which I belive it is supposed to call.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.