AMN and math logic notation - math

I'm not sure this is appropriate for stackoverflow, but I don't know where else to ask. I'm studying the B-Method for proving consistence in requirement specifications, and I have an issue with the logic math notation when specifying the pre conditions of the operations.
Simplifying the original problem, I have a variable which is a subset flights of the cartesian product between FLIGHT_NO x TIME x TIME, where for each member (no,td,ta), no means the number of the flight, td the time of departure and ta the tme of arrival. How can I get, using math logic notation, the element of flights that has the biggest value of td?

Do you want to get such an element, or to test that an element you have satisfies this property? I am asking because the second seems a sensible precondition to an operation. I don't know the B-Method specifically; I've looked at some documents, but can't find a quick reference, so this may be wrong in some details.
The second should look like this (prj2 is used for the second projection):
HasGreatestTd(flight) = flight \in flights \and \forall flight' (flight' \in flights => prj2(flight) >= prj2(flight'))
Then the first is:
flightWithGreatestTd = choice({flight | HasGreatestTd(flight)})

Forgive my ignorance, I'm not familiar with the B-Method. But couldn't you use the uniqueness quantifier? It'd look something like:
there exists a time td such that for all times td', td > td'
and
for all td, td', td'', if td > td'' and td' > td'' then td == td'
This, of course, assumes that there is exactly one element in the set. I can't really tell if the B-Method allows for the full power of first order logic but I assume you could come close to this.

It is possible to define functions in B. Functions have constant values and are to be listed in the ABSTRACT_CONSTANTS clause, and defined in the PROPERTIES clause. I try to explain how you can use this construct to solve your problem.
Follows a small excerpt where
a shortcut for the cartesian product giving flight information is introduced;
DEFINITIONS
FLIGHT_INFO == FLIGHT_NO * TIME * TIME
four constants are declared, the first three are "accessors", and the last maps a non-empty set of flight informations to the flight information with the largest departure time.
CONSTANTS
flight_no, flight_departure, flight_arrival, last_flight
Then these constants are typed and defined as total functions. Note that the last function must take as input a non-empty set. Here I used to different approaches to specify these functions. One is definitional (with an equality), and the other is axiomatic.
PROPERTIES
// typing
flight_no: FLIGHT_INFO --> FLIGHT_NO &
flight_departure: FLIGHT_INFO --> TIME &
flight_arrival: FLIGHT_INFO --> TIME &
last_flight : POW1(FLIGHT_INFO) --> FLIGHT_INFO &
// value
flight_no = %(no, dt, at).(no |-> dt |-> at : FLIGHT_INFO | no) &
flight_departure = %(no, dt, at).(no |-> dt |-> at : FLIGHT_INFO | dt) &
flight_arrival = %(no, dt, at).(no |-> dt |-> at : FLIGHT_INFO | at) &
!fs.(fs : POW1(FLIGHT_INFO) =>
last_flight(fs) : fs &
!(fi).(fi : FLIGHT_INFO & fi : fs =>
flight_departure(fi) <= flight_departure(last_flight(fs)))

Related

Xquery - How to match two sequences within a quantifier expression

Like many, I'm tackling the Mondial database on XML. It would be a piece of cake, if XQuery syntax wasn't doing its best to sabotage.
let $inland := //province/#id
where every $sea in //sea satisfies
$sea/located/#province != $inland
return $inland
What I am trying to do in the above is find all "inland" provinces, the provinces that don't have a sea next to it. This, however, doesn't work, because the $sea/located/province is a big string, with every single province that it borders in it.
So I tried to modify into.
let $inland := //province/#id
where every $sea in //sea satisfies
not(contains($sea/located/#province, $inland))
return $inland
Where I would like it to only find the provinces that are a part of the sea's bordering provinces. Simple and straightforward.
Error message:
Stopped at C:/Users/saffekaffe/Desktop/mondial/xml/country_without_island.xml, 2/1:
[XPTY0004] Item expected, sequence found: (attribute id {"prov-Greece-2"},....
How do I get around this?
Example of //sea/located/province#
province="prov-France-5 prov-France-20 prov-France-89 prov-France-99"
Example of //province/#id
id="prov-Greece-2"
There are multiple ways in which XQuery works in a different way than you seem to expect.
The comparison operators = and != have existential semantics if at least one of their arguments is a sequence instead of a single item. This means that $seq1 = $seq2 is equivalent to some $x in $seq1, $y in $seq2 satisfies $x = $y. The query ('foo', 'bar') = ('bar', 'baz', 'quuz') returns true because there is at least one common item.
An XQuery exception like //province/#id evaluates to a sequence of all matching nodes. In your case that would be a sequence of over 1000 province IDs: (id="prov-cid-cia-Greece-2", id="prov-cid-cia-Greece-3", id="prov-cid-cia-Greece-4", [...]). This sequence is then bound to the variable $inland in your let clause. Since you don't iterate over individual items in $inland (for example using a for clause), the where condition then works on the whole sequence of all provinces worldwide at once. So your condition every $sea in //sea satisfies
$sea/located/#province != $inland now means:
"For every sea there is a province located next to it that has an #id that is not equal to at least one of all existing province IDs."
Th is returns false because there are seas with no located children, e.g.the Gulf of Aden.
contains($str, $sub) is not a good fit for checking if a substring is contained in a space-delimited string, because it also matches parts of entries: contains("foobar baz quux", "oob") returns true.
Instead you should either split the string into its parts using tokenize($str) and look through its parts, or use contains-token($str, $token).
Putting it all together, a correct query very similar to your original one is:
for $inland in //province/#id
where
every $sea in //sea
satisfies not(contains-token($sea/located/#province, $inland))
return $inland
Another approach would be to first gather all (unique) provinces that are next to seas and then return all provinces not in that sequence:
let $next-to-sea := distinct-values(//sea/located/#province/tokenize(.))
return //province/#id[not(. = $next-to-sea)]
Even more compact (but potentially less efficient):
//province/#id[not(. = //sea/located/#province/tokenize(.))]
On the other end of the spectrum you can use XQuery 3.0 maps to replace the potentially linear search through all seaside provinces by a single lookup:
let $seaside :=
map:merge(
for $id in //sea/located/#province/tokenize(.)
return map{ $id: () }
)
return //province/#id[not(map:contains($seaside, .))]

Counting nr of elements in a file

I am trying to count the number of Harbour elements in an XML file. However, i keep getting the following error:
item expected, sequence found: (element harbour {...}, ...)
The code snippet is the following:
for $harbour in distinct-values(/VOC/voyage/leftpage/harbour)
let $count := count(/VOC/voyage/leftpage/harbour eq $harbour)
return concat($harbour, " ", $count)
Input XML:
<voyage>
<number>4411</number>
<leftpage>
<harbour>Rammekens</harbour>
</leftpage>
</voyage>
<voyage>
<number>4412</number>
<leftpage>
<harbour>Texel</harbour>
</leftpage>
</voyage>
Can someone help me out? How do I iterate over the number of harbours in the XML file instead of trying to use /VOC/voyage/leftpage/harbour?
eq is a value comparison, i.e. used to compare individual items. That is why the errors messages tells you that it is expecting a (single) item, but instead found all the harbour elements. You have to use the general comparison operator =. Also, when you would compare it like that
/VOC/voyage/leftpage/harbour = $harbour
it would always be 1 as it will compare the existence. instead, you want to filter out all harbour items which have an equal text element as child. You can do so using []. All together it will be
for $harbour in distinct-values(/VOC/voyage/leftpage/harbour)
let $count := count(/VOC/voyage/leftpage/harbour[. = $harbour])
return concat($harbour, " ", $count)
Also, if your XQuery processor supports XQuery 3.0 you can also use a group by operator, which in my opinion is nicer to read (and could be faster, but this depends on the implementation):
for $voyage in /VOC/voyage
let $harbour := $voyage/leftpage/harbour
let $harbour-name := $harbour/string()
group by $harbour-name
return $harbour-name || " " || count($harbour)

Specman e: Is there a way to constrain the amount of set bits in a number?

I have unit field events:
events:uint;
The values of events not so interesting as the amount of set bits in it. I would like to constrain the ranges of the amount of set bits in the events.
Is there a way to do it?
Thank you for your help.
The operations [..] and %{} aren't generative therefore they considered as inputs in the constraints.
The constraint:
keep events_bits == events[..];
is equivalent to:
keep events_bits == read_only(events[..]);
The generator will generate events and only then will enforce the constraints on `events_bits.
You can do the following:
extend sys {
events : uint;
events_bits_on [32] : list of bool;
keep for each in events_bits_on {
it == (events[index:index] == 1);
};
keep events_bits_on.count(it) == 2;
};
There might be an easier way to do it, but you can use bit slicing to constrain a list of uints to equal the bits of your variable, and use sum to constrain their amount.
The following example does what you need (limited to a 4 bit variable for brevity):
<'
extend sys {
events : uint(bits:4);
b: list of uint(bits:1);
keep b.size() == 4;
keep b[0] == events[0:0];
keep b[1] == events[1:1];
keep b[2] == events[2:2];
keep b[3] == events[3:3];
keep (b.sum(it) == 2);
};
'>
Writing all the constraints is probably a little ugly, but it can easily be done using a define as computed macro.
This is only a partial answer.
You can use events[..] or %{events} to convert from a vector to a list containing the bits of that vector. Using it directly in a constraint directly doesn't work, because it complains there's no generative element:
extend sys {
events : uint(bits:4);
// neither of these compile
keep events[..].sum(it) == value(2);
keep %{events}.sum(it) == value(2);
};
This is probably a case for Cadence.
What is allowed however is to create an intermediate list and assign that to the output of either of these operators:
extend sys {
events_bits : list of bit;
keep events_bits == events[..];
};
You would think that you could then constrain this list to have a certain number of 1s:
extend sys {
// these constraints apply, but aren't enforced
keep events_bits.sum(it) == value(2);
keep events_bits.count(it == 1) == value(2);
};
This doesn't work however. Even though the constraints are there, they don't get enforced for some reason. This is another issue for Cadence to look at.
Summarizing, if these issues weren't there, you could probably count the number of ones easily. Maybe in a future version of Specman. I still hope that at least seeing that the [..] and the %{} operators exist will help you for other things.

F# Recursive Tree Validation

This is a somewhat beginner question. I have been trying to validate the following type of FamilyTree. I can't find a simple way to do this. All help would be appreciated.
type BirthYear = int;;
type Tree = Person of BirthYear * Children
and Children = Tree list;;
I want to validate a given family tree such that every Person is older than their Children and furthermore check if the list of Children is sorted in order of their age (eldest first). Preferably done with a function that return a boolean. Something along the lines of this:
let rec validate (Person(x,child)) =
let vali = child |> List.forall (fun (y,_) -> y < x)
I'd do something like this:
let rec checkAges minBirth = function
| Person(b, _) :: t -> b >= minBirth && checkAges b t
| [] -> true
let rec validate (Person(b, c)) =
List.forall validate c && checkAges (b + minParentAge) c
where minParentAge is set to a reasonable minimum age to have children at.
I'd expect checkAges to be the more difficult part here: the function checks whether the first child it sees is younger than the limit it is given, then recursively checks the next child, with the current child's age as the new limit.
Note some techniques:
The function that checks child ages takes the minimum birthday as input; this is used to validate that the parent is old enough for the first child to be reasonable.
List.forall checks a predicate for all items in a list, and early-outs if a predicate is not fulfilled
function is a shorthand to create a function that does pattern matching on its parameter. Therefore, checkAges actually has two arguments.
Here's a very simple solution using a single recursive function. It's not relying on built-in functions like List.forall but I think it's very declarative and (hopefully) easy to follow.
Rule 1: Every Person is older than their Children
Rule 2: List of Children is sorted in order of their age (eldest first)
Code:
let rec isValid = function
| Person ( _ , []) -> true // Person alone without childs -> always valid
| Person (minYear, Person (year, childs) :: brothers) ->
year > minYear && // Validate Rules (either 1 or 2)
isValid (Person (year, childs)) && // Enforce Rule 1
isValid (Person (year, brothers)) // Enforce Rule 2
I personally don't feel List.forall fits well here, it helps to solve a part of the problem but not the whole, so you need to combine it with more stuff (see the other answers) and in the end you can't avoid a recursive function.
List functions are good for lists but for trees I feel recursion more natural unless your tree provides already a way to traverse it.
Here's a way to do it. Perhaps spending some time analyzing how this works will be helpful to you.
let rec check (Person(age, children)) =
match children with
| [] -> true
| Person(eldest, _)::_ ->
Seq.pairwise children |> Seq.forall ((<||) (>))
&& age > eldest
&& List.forall check children

pyparsing for querying a database of chemical elements

I would like to parse a query for a database of chemical elements.
The database is stored in a xml file. Parsing that file produces a nested dictionary that is stored in a singleton object that inherit from collections.OrderedDict.
Asking for an element will give me an ordered dictionary of its corresponding properties
(i.e. ELEMENTS['C'] --> {'name':'carbon','neutron' : 0,'proton':6, ...}).
Conversely, asking for a propery will give me an ordered dictionary of its values for all the elements (i.e. ELEMENTS['proton'] --> {'H' : 1, 'He' : 2} ...).
A typical query could be:
mass > 10 or (nucleon < 20 and atomic_radius < 5)
where each 'subquery' (i.e. mass > 10) will return the set of elements that matches it.
Then, the query will be converted and transformed internally to a string that will be evaluated further to produce a set of the indexes of the elements that matched it. In that context the operators and/or are not boolean operator but rather ensemble operator that acts upon python sets.
I recently sent a post for building such a query. Thanks to the useful answers I got, I think that I did more or less the job (I hope on a nice way !) but I still have some questions related to pyparsing.
Here is my code:
import numpy
from pyparsing import *
# This import a singleton object storing the datase dictionary as
# described earlier
from ElementsDatabase import ELEMENTS
and_operator = oneOf(['and','&'], caseless=True)
or_operator = oneOf(['or' ,'|'], caseless=True)
# ELEMENTS.properties is a property getter that returns the list of
# registered properties in the database
props = oneOf(ELEMENTS.properties, caseless=True)
# A property keyword can be quoted or not.
props = Suppress('"') + props + Suppress('"') | props
# When parsed, it must be replaced by the following expression that
# will be eval later.
props.setParseAction(lambda t : "numpy.array(ELEMENTS['%s'].values())" % t[0].lower())
quote = QuotedString('"')
integer = Regex(r'[+-]?\d+').setParseAction(lambda t:int(t[0]))
float_ = Regex(r'[+-]?(\d+(\.\d*)?)?([eE][+-]?\d+)?').setParseAction(lambda t:float(t[0]))
comparison_operator = oneOf(['==','!=','>','>=','<', '<='])
comparison_expr = props + comparison_operator + (quote | float_ | integer)
comparison_expr.setParseAction(lambda t : "set(numpy.where(%s)%s%s)" % tuple(t))
grammar = Combine(operatorPrecedence(comparison_expr, [(and_operator, 2, opAssoc.LEFT) (or_operator, 2, opAssoc.LEFT)]))
# A test query
res = grammar.parseString('"mass " > 30 or (nucleon == 1)',parseAll=True)
print eval(' '.join(res._asStringList()))
My question are the following:
1 using 'transformString' instead of 'parseString' never triggers any
exception even when the string to be parsed does not match the grammar.
However, it is exactly the functionnality I need. Is there is a way to do so ?
2 I would like to reintroduce white spaces between my tokens in order
that my eval does not fail. The only way I found to do so it the one
implemented above. Would you see a better way using pyparsing ?
sorry for the long post but I wanted to introduce in deeper details its context. BTW, if you find this approach bad, do not hesitate to tell it me!
thank you very much for your help.
Eric
do not worry about my concern, I found a work around. I used the SimpleBool.py example shipped with pyparsing (thanks for the hint Paul).
Basically, I used the following approach:
1 for each subquery (i.e. mass > 10), using the setParseAction method,
I joined a function that returns the set of eleements that matched
the subquery
2 then, I joined the following functions for each logical operator (and,
or and not):
def not_operator(token):
_, s = token[0]
# ELEMENTS is the singleton described in my original post
return set(ELEMENTS.keys()).difference(s)
def and_operator(token):
s1, _, s2 = token[0]
return (s1 and s2)
def or_operator(token):
s1, _, s2 = token[0]
return (s1 or s2)
# Thanks for Paul for the hint.
grammar = operatorPrecedence(comparison_expr,
[(not_token, 1,opAssoc.RIGHT,not_operator),
(and_token, 2, opAssoc.LEFT,and_operator),
(or_token, 2, opAssoc.LEFT,or_operator)])
Please not that these operators acts upon python sets rather than
on booleans.
And that does the job.
I hope that this approach will help anyone of you.
Eric

Resources