:- expects_dialect(lps). /* Adapted by RAK from the simplified loan agreement in Flood and Goodenough, 2015. Contract as automaton: the computational representation of financial agreements. https://www.financialresearch.gov/working-papers/files/OFRwp-2015-04_Contract-as-Automaton-The-Computational-Representation-of-Financial-Agreements.pdf This representation is similar in spirit to the Flood_Goodenough formalisation as a collection of current state -> event -> new state transitions, represented in LPS by clauses of the form: event initiates new fluent if current current fluents. Please note, occasionally some fluents might be displayed in the timeline above events. */ maxTime(22). fluents valid_contract, requested/2, advanced/2, covenant/1, represent_warrant/1, paid/3, tax_due/1, true/1, notified/3, potential_defaulted/1, cured/1, defaulted/2, remedied/1, due_payable/2, total_due/1. events end_of_day/1, request/2, advance/2, pay/3, violate/1, bankruptcy_insolvency/1, notify/3. actions legal_action_against/1, terminate_correctly, cancel. % Note event initiates fluent is a convenient way to remember that an event has happened, % It may be possible to add the time of the event as a parameter of the fluent. request(Person, Amount) initiates requested(Person, Amount). advance(Person, Amount) initiates advanced(Person, Amount). pay(Borrower, Lender, Amount) initiates paid(Borrower, Lender, Amount). notify(Person, Requirement, Date) initiates notified(Person, Requirement, Date). terminate_correctly terminates valid_contract. cancel terminates valid_contract. legal_action_against(_) terminates valid_contract. initially valid_contract. % true(exceed( assets, liabilities)). % simulation of time for testing. % % temporary for testing. add(Day1/Month/Year, N, Day2/Month/Year) :- Day2 is Day1 + N. % For simplicity, one cycle represents one day. % end_of_day(D) from T to T+1 means day D coincides with T, T+1 is the next day. % An event (such as a payment) happening on day D is represented as happening from T-1 to T. % So any fluent (such as the payment being paid) initiated by the event holds on day D. % % To explore alternative scenaria, comment and uncomment observations. % Notice that not every scenario makes sense. % For example, repaying a loan that has not been advanced makes no sense. % These can be rejected by adding preconditions/constraints. % Or can be compensated by adding reactive rules, e.g. returning payments. % % Try, for example, borrower doesn't pay anything back, but lender does not notify default. observe end_of_day(1/6/2014) from 2 to 3. % borrower needs to have requested $1000 at 2. observe end_of_day(2/6/2014) from 3 to 4. % lender needs to have advanced $1000 at 3. observe end_of_day(1/6/2015) from 4 to 5. % borrower needs to have paid $550 at 4. observe end_of_day(2/6/2015) from 5 to 6. % lender might notify potential default to 5. observe end_of_day(4/6/2015) from 8 to 9. % borrower might cure potential default to 8. observe end_of_day(5/6/2015) from 9 to 10. % if borrower defaults, all payments due to 9. observe end_of_day(1/6/2016) from 11 to 12. % borrower needs to have paid $525 at 11. observe end_of_day(5/6/2016) from 14 to 15. % lender might notify potential default to 14. observe end_of_day(7/6/2016) from 16 to 17. % borrower might cure potential default to 16. observe end_of_day(8/6/2016) from 18 to 19. % if borrower defaults, all payments due to 18. observe end_of_day(1/6/2020) from 20 to 21. % stature of limitations. observe request(borrower, 1000) from 1 to 2. % request on time. observe advance(lender, 1000) from 2 to 3. % advance on time % observe pay(borrower, lender, 550) from 3 to 4. % pay on time. observe notify(lender, default(pay(borrower, lender, 550)), 2/6/2015) from 4 to 5. observe pay(borrower, lender, 550) from 6 to 7. % must pay before giving notice. observe notify(borrower, remedy(pay(borrower, lender, 550)), 4/6/2015) from 7 to 8. % observe pay(borrower, lender, 525) from 10 to 11. % pay on time. observe notify(lender, default(pay(borrower, lender, 525)), 5/6/2016) from 13 to 14. observe pay(borrower, lender, 525) from 17 to 18. % borrower pays one day after default. % Notice that if borrower defaults, by paying but not notifying the payment, % the payment does not become due, and the contract terminates "correctly". % The next two rules could be initiates-postcondition clauses. if end_of_day(1/6/2014) to T, not requested(borrower, 1000) at T then terminate_correctly from T. if end_of_day(2/6/2014) to T, requested(borrower, 1000) at T, not advanced(lender, 1000) at T then legal_action_against(lender) from T. due(550, 1/6/2015). % $550 should be paid by the end of the day. due(525, 1/6/2016). end_of_day(Date) initiates potential_defaulted(pay(borrower, lender, Amount)) if valid_contract, due(Amount, Date), not paid(borrower, lender, Amount). % Note that in a more refined representation, % violate could be a macro-event defined by a metainterpreter: % For example, violate(Requirement) from T1 to T2 % if true(Requirement) at T1, not true(Requirement) at T2. % true(P if Q) at T if true(P) at T, not true(Q) at T. % true(P at T1) at T if T = T1, P at T. % etc. /* initially represent_warrant( exceed( assets, liabilities) at T if due(Payment, Date), end_of_day(Date) at T). initially represent_warrant( exceed( assets, liabilities) at T if requested(borrower, 1000) at T). initially covenant(pay_authority(Amount) at T if tax_due(Amount) at T). */ represent_warrant('assets exceed liabilities if borrower requests funds or a payment is due'). covenant('pay tax when due if borrower requests funds or a payment is due'). violate(Requirement) initiates potential_defaulted(Requirement) if valid_contract, represent_warrant( Requirement ). violate(Requirement) initiates potential_defaulted( Requirement) if valid_contract, covenant( Requirement ). bankruptcy_insolvency(borrower) initiates potential_defaulted( solvent_not_bankrupt(borrower)) if valid_contract. end_of_day(Date2) initiates defaulted(Requirement, Date2) if potential_defaulted( Requirement), notified(lender, default(Requirement), Date1), add(Date1, 2, Date2), not defaulted(_, _), not cured(Requirement). cured(Requirement) at T if remedied(Requirement) at T, notified(borrower, remedy(Requirement), _). /* Example of one case of remedy for the moment. More generally we could have: remedy(Requirement) from T1 to T2 if potential_defaulted(Requirement) at T1, true(Requirement) at T2. remedy(Requirement) initiates remedied(Requirement). */ pay(borrower, lender, Amount) initiates remedied(pay(borrower, lender, Amount)) if potential_defaulted(pay(borrower, lender, Amount)). end_of_day(Date2) initiates due_payable(Sum, Date2) if defaulted(pay(borrower, lender, Payment), Date1), add(Date1, 1, Date2), total_due(Sum). total_due(1075) at T if not paid(borrower, lender, 550) at T. total_due(525) at T if paid(borrower, lender, 550) at T, not paid(borrower, lender, 525) at T. % Alternatively, replace the following four rules by initiates postcondition fluent clauese. if end_of_day(Date) to T, due_payable(Sum, Date) at T, paid(borrower, lender, Sum) at T then terminate_correctly from T. if due_payable(Sum, Date) at T, valid_contract at T, Date \= 1/6/2020, % better to compare Date < 1/6/2020. not paid(borrower, lender, Sum) at T then legal_action_against(owes(borrower, Sum)) from T. if valid_contract at T, end_of_day(1/6/2020) to T then cancel from T. if pay(borrower, lender, 525) to T, valid_contract at T, paid(borrower, lender, 550) at T then terminate_correctly from T. /** ?- go(Timeline). */