This wiki has undergone a migration to Confluence found Here
<meta name="googlebot" content="noindex">

Difference between revisions of "Object Equivalence, determination of"

From HL7Wiki
Jump to navigation Jump to search
 
(23 intermediate revisions by 2 users not shown)
Line 1: Line 1:
[[CATEGORY:RIMBAA Issue]]
+
[[CATEGORY:Closed AID Issue]]
 +
[[Category:MnM Hot Topic]]
 +
[[Category:MnM Open Hot Topic]]
 +
 
 +
==FHIR Notes==
 +
We'll probably need some guidance as to how to determine 'Resource Equivalence', especially given that all componehts of the [http://hl7.org/fhir/2015May/datatypes.html#Identifier Identifier datatype] are optional. Does the scoper still play a role in the context of some resources, such as Patient ?
 +
*Created new page [[Resource Equivalence]]
 +
 
 
==Overview==
 
==Overview==
 
The Object Identity (a.k.a. Business Identity, identity throughout the business cycle) of an object uniquely identifies the object. Two objects (e.g. as present in two different messages) with the same Object Identity are one and the same object.
 
The Object Identity (a.k.a. Business Identity, identity throughout the business cycle) of an object uniquely identifies the object. Two objects (e.g. as present in two different messages) with the same Object Identity are one and the same object.
  
The business identity of a RIM class is contained in its Id attribute (for Acts, Entities and -managed- Participations).  
+
===Identity and Equality===
*For Roles the situation is a bit of a muddle, because one doesn't know if the Role.id is a unique identifier of this single role instance, or whether it is an identifier used for a set of role instances. See section below for details.
+
 
*For certain Entities the identity may actually be present in the ''code'' instead of the ''id'' attribute (e.g. for Countries). See section below for details.
+
Definitions:
*A significant number of objects in a RIM-based object tree (e.g a message) don't have a populated id attribute. Example: most participations and non-focal observations. In specific circunstances the Object Identity can be determined by the context (the set of related objects) of the object in question. See section below for details.
+
* '''Identical''': Two objects are identical if all their properties are equal, and they cannot be distinguished
 +
* '''Equal''':  Two objects are equal if their identifying characteristics are equal.
 +
* '''Equivalent''': Two objects are equivalent if their properties are different, but it is known that they refer to the same real world concept
 +
 
 +
===Scope of Identifier===
 +
An identifier (the ''identifying characteristic'', if expressed as an II data type in HL7) can be of different types (have different characteristics). The Datatpes R2 specification documents the II.scope (CS datatype) attribute, which is defined as ''Specifies the scope in which the identifier applies to the object with which it is associated.''.
 +
*The CodeSystem used is "IdentifierScope", with OID: 2.16.840.1.113883.5.1116. Values are:
 +
**BUSN: Business Identifier. An identifier whose scope is defined by business practices associated with the object. In contrast to the other scope identifiers, the scope of the use of the id is not necessarily restricted to a single object, but may be reused for other objects closely associated with the object due to business practice.
 +
**OBJ: Object Identifier. The identifier associated with a particular object. It remains consistent as the object undergoes state transitions.
 +
**VER: Version Identifier. An identifier that references a particular object as it existed at a given point in time. The identifier SHALL change with each state transition on the object. I.e. The version identifier of an object prior to a 'suspend' state transition is distinct from the identifier of the object after the state transition. Each version identifier can be tied to exactly one ControlAct event which brought that version into being (though the control act may never be instantiated). NOTE: Applications that do not support versioning of objects must ignore and not persist these ids to avoid confusion resulting from leaving the same identifier on an object that undergoes changes.
 +
**VW: View Specific Identifier. An identifier for a particular snapshot of a version of the object. This identifies a view of the business object at a particular point in time, and as such identifies a set of data items that can be digitally signed and/or attested. This is in contrast to the Version Identifier which identifies the object at a specific time, but not the amount of information being asserted about the object. This identifier would be changed when a transformation of the information is performed (eg to add code translations, to provide a simplified textual rendering, or to provide additional information about the object as it existed at the specific point in time).
 +
 
 +
==Guidance, to be added to Core Principles==
 +
The rules for determining when matching identifiers can lead to certainty about equivalence of the objects being compared are unclear. 
 +
 
 +
The following guidance was developed during an MnM meeting in Sydney (20110111, Q3),
 +
this guidance will be added to a future version of the Core Principles document.
 +
 
 +
===Role equivalence===
 +
Role identifiers can be considered to refer to identical role objects IFF:
 +
*There are II values on each of the roles that are equivalent according to the II equivalence definition
 +
*Both IIs declare the scope property
 +
*Both scope properties are one of OBJ, VER or VIEW (they need not be the same on both instances)
 +
In all other circumstances, object equivalence cannot be safely assumed without specific knowledge of the implementation environment, no matter how many other attributes are compared.  (It may still be possible to achieve "high probability matches" with less information.)
 +
 
 +
Example: Clinic uses Social Security Number as their "public" patient identifiers plus an internal object identifier.  Patient comes to clinic for several years, then leaves for several years and the role gets flagged as "terminated". They then come back to the clinic and a new role is created for the patient, still using the same SSN as the "public identifier, but with a new internal object identifier.  Eventually they leave again and the status becomes terminated again.  The SSN cannot be used to point to a single role object instance, even when paired with classCode, code, status, scoper, etc.
 +
 
 +
Playing Entities of Roles with equivalent Role identifiers can be considered to be equivalent.
 +
 
 +
===Entity equivalence===
 +
The same rules for determining equivalency apply as for roles (equivalent IIs each with declared scopes other than BUSN).
 +
 
 +
In addition, in some cases an entity may be uniquely identified by the code.  This may occur for entities with determinerCode of INSTANCE (e.g. countries) as well as those with determinerCode of KIND (e.g. drugs).  This equivalence cannot be safely asserted without specific knowledge of the implementation environment.
 +
 
 +
For example, the ISO country codes might be used to "identify" both country codes (geographic region) and nation codes (political organization).  However, the geographic entity object of type X is not equivalent to the nation entity object of type X.
 +
 
 +
Note: If an Entity II does not have a declared scope, in most circumstances it will be reasonable to presume it is of type "OBJ" and can be treated accordingly.
 +
 
 +
===Act equivalence===
 +
The same rules for determining equivalency apply on Acts as for Roles (equivalent IIs each with declared scopes other than BUSN).
 +
 
 +
Note: If an Act II does not have a declared scope, in most circumstances it will be reasonable to presume it is of type "OBJ" and can be treated accordingly.
 +
 
 +
===Participation and RoleLink equivalence===
 +
The scope property for IIs on Participations and RoleLinks must never be BUSN or VIEW.  As such, the IIs can always be presumed to be comparable.  The sole equivalence requirement is that the objects have equivalent IIs.
 +
 
 +
==Additional guidance for software implementers (to be updated)==
 +
The wording in the above section applies at the universal level without one being aware of the context/setting of where the HL7 v3 objects are being used.  
 +
 
 +
The following also applies at the universal leve:
 +
*The identity of the Role depends on the identity of the playing and scoping entities; those identities may not be known.
 +
**If the playing entities are both present but not equivalent, the roles being played by these entities are (by definition) not equivalent.
 +
**It's a business rule if changing the scoper requires a new role, and therefore the identity of the scoper can't be used to determine equivalence of roles.
 +
 
 +
Equivalence can only really be determined if one knows the value II.scope. HL7 v3 implementers (RIMBAA implementers) that use Datatypes R1 should pre-adopt II.scope, i.e. support that concept in their application even if the wire format doesn't. In most circumstances (though not universally so, see below) the value of II.scope can be determined by looking at II.root. See section below for details.
 +
 
 +
On occasion Entities may be identified through Entity.code instead of Entity.id. If the concept codes in a coding system identifies a single object (e.g. then this is known as ''appellation'' (according to ISO 704). In most circumstances (though not universally so, see below) the value of C?.codingSystem (an OID) can be used to determine if it acts as an identifier. See section below for details.
 +
 
 +
There are implementations where Role identifiers are 'pushed into' the entity; the role object itself doesn't have a populated id. One can reasonably assume the role.id to be the same as the entity.id.
 +
 
 +
If the Role.id of at least one Role object has an unknown II.scope, and both IIs are equivalent, then two Role objects can only be said to be identical if: scoping entities are identical AND playing entities are identical AND code/classCode of the Role objects are the same, THEN one could reasonably conclude that two Role objects are the same.
 +
 
 +
All RIMBAA applications, in order to decide if two roles are the equivalent will have to build some kind of "role identity management component".
 +
 
 +
Models may contain 'Act References', 'Role References'. These objects are rather minimalistic in nature, they contain an ID, no code attribute, and a generic classCode/moodCode. In this situation the ID really has to be a single Act instance (OBJ, VER, VIEW). If the ID is a business identifier the reference object won't be very useful.
 +
 
 +
===II.root and II.scope===
 +
It is tempting to look at the root OID, and if it is equal to (for example) the OID of the US SSN identification scheme (a widely used identifier for all sorts of roles), to state that the II.scope is therefore equal to BUSN.
 +
 
 +
At the universal level this however isn't true. For the above example it depends on the scoper of the Role: if the scoper is the US Governement (who assigns SSNs), then the II.scope will be OBJ (the US Governement uniquely identifies one single citizen by means of an SSN). If the scoper is a hospital, the use of SSN is as a BUSN identifier. There are also circumstances where (e.g. for Acts) one systems OBJ scoped II is another systems VER scoped II. 
 +
 
 +
Whilst one may not be able to deduce the value of II.scope from II.root at the universal level it is very likely that one is able to do so within a single project/context. Within a specific context one will be aware if (for example) the scoper of a role identified with an SSN will ever be the US Government. If not, then the SSN root OID implies that the scope of the identifier is BUSN.
  
===Object Identity for Roles===
+
===Entity.code and appellations===
For Roles the situation is a bit of a muddle, because one doesn't know if the Role.id is a unique identifier of this single role instance, or whether it is an identifier used for a set of role instances. An example of the latter is a national identifier (e.g. SSN in the USA) to identify multiple roles. In those cases the identity of the role is based on the tuple (Role.id, 'Scoper') where 'Scoper' could be the Entity.id or the Entity.name of the scoping entity.
+
It is tempting to look at the OID of the coding system, and if it is equal to (for example) the OID of the ISO Country coding system, to state that the code is an appellation (i.e. that the code is an identifier with scope OBJ).
*This is especially relevant in the process of deciding when to 'merge' objects into an object net.
 
**Example #1: Dr. Smith is a part-time employee of two different hospitals, A and B. Within each organization he performs a different role (different role.code values). Each role has different contact details, address etc. However: both hospitals (the scopers of the respective roles) both know Dr. Smith using one and the same ID: his 'national healthcare provide identity', 607802. Given that the roles are different, but the Role.id values are the same (607802), one has to use the scoper.Id (A or B) in combination with the Role.id to uniquely identify a role instance.
 
**Example #2: a RIMBAA application persists an ''Observation with subject Patient 123 with scoping entity 'hospital A'''.
 
**#If it subsequently receives an object tree with ''Demographics update for Patient 123 with scoping entity 'hospital A''' - in that case the patient objects are one and the same.
 
**#If it subsequently receives an object tree with ''Procedure with subject Patient 123 with scoping entity 'hospital B''' - the objects won't be merged - they're not the same.
 
**#If it subsequently receives an object tree with ''Demographics update for Patient 123 without a scoping entity being present''' - the objects won't be merged, the scoper of the new object is unknown and therefore can't be merged automatically with any other Role object.
 
  
As a consequence of the role identity issue described above (''this is an open methodology issue''), all RIMBAA applications, in order to decide if two roles are the same (one and the same object, just different versions of it) will have to build some kind of "role identity management component". Roles may or may not have an id, scopers may or may not be present, scopers may not have an id, the id may be specified via an IDENT role, the entity may have an ID whereas the Role it plays has not, the ID of the entity may have to be gleaned of another role played by the entity.. identity crisis, indeed.
+
At the universal level this however isn't true. For example, the ISO country codes might be used to "identify" both Country entities (geographic region) as well as Nation entities (political organization). However, the geographic entity object of type X is not equivalent to the nation entity object of type X.
  
===Object Identity for Entities===
+
Whilst one may not be able to deduce whether or not a code is an appellation at the universal level it is very likely that one is able to do so within a single project/context. Within a specific context one will be aware that (for example) Country entities are never used. In that case the codeSystem OID for the ISO Country coding system implies that the code always has the appellation characteristic.
The [[Core Principles]] document (CP) provides guidance as to whether something is a code or an identifier. There are circumstances where there is little difference between the two, in which case one could use either (see section X.Y of the CP document).
 
  
Example: Countries. ISO has defined a code system for country codes, to identify the "type of country" (e.g. US for USA, NL for Netherlands etc.). This coding system is used in the entity.code attribute - to identify a country.
+
==Pseudo code==
 +
The pseudo code below captures the recommendations as shown above.
  
===Contextual Object id===
+
# A and B are both objects of any RIM class. How do we determine equivalence?
A significant number of objects in a RIM-based object tree (e.g a message) don't have a populated id attribute. Example: most participations and non-focal observations. In specific circunstances the Object Identity can be determined by the context (the set of related objects) of the object in question.
+
 +
Function ObjectsEquivalent->boolean (A,B) {
 +
 +
    #----- Phase 1: fudge missing object.id values if possible
 +
    if isEntity(A) and not populated(A.id) and populated(A.code) {
 +
      #code could be an appellation
 +
      if usesAppellation(A.code) {
 +
            set A.id/@root = A.code/@codeSystem
 +
            set A.id/@extension = A.code/@code
 +
            set A.id/@scope = "OBJ"
 +
      }
 +
    }
 +
    if isEntity(B) and not populated(B.id) and populated(B.code) {
 +
      #code could be an appellation
 +
      if usesAppellation(B.code) {
 +
            set B.id/@root = B.code/@codeSystem
 +
            set B.id/@extension = B.code/@code
 +
            set B.id/@scope = "OBJ"
 +
      }
 +
    }
 +
    if isRole(A) and not populated(A.id) { set A.id = A.playingEntity.id }
 +
    if isRole(B) and not populated(A.id) { set A.id = A.playingEntity.id }
 +
   
 +
    #----- Phase 2: determine equivalence based on the IIs of the two objects
 +
    if populated(A.id) and populated(B.id) {
 +
   
 +
        if (A.id/@root = B.id/@root) and (A.id/@extension = B.id/@extension) {
 +
            # II equivalence
 +
            if DataypesR2 and (A.id/@scope = B.id/@scope) and not (B.id/@scope = BUSN) {
 +
                # stringest form of equivalency, jniversally true
 +
                return "true"
 +
            } else {
 +
                # II.scope only matters for Role.id
 +
                # for RoleLinks and Participations scope by definition will never be BUSN
 +
                # for Acts and Entities one can rasonably assume it not to be a BUSN identifier
 +
                if not isRole(A) {
 +
                  return "true"
 +
                } else {
 +
                    # fudge/kludge missing id/@scope values (if any) -
 +
                    # may not be possible, may not be reliable
 +
                    if not populated (A.id/@scope) { set A.id/@scope = determineScopeFor(A.id) }
 +
                    if not populated (B.id/@scope) { set B.id/@scope = determineScopeFor(B.id) }
 +
                    # for Role equivalence, scope has to be equal, but not BUSN
 +
                    if (A.id/@scope = B.id/@scope) and not (A.id/@scope = BUSN) {
 +
                      return "true"
 +
                    } else {
 +
                      return "false"
 +
                    }
 +
                }
 +
            }
 +
        } else {
 +
          return "false"
 +
        }
 +
   
 +
    } else {
 +
      # at least one id is missing, one or both ids not populated
 +
      return "false"
 +
    }
 +
 +
}
 +
&nbsp;
 +
&nbsp;
 +
Function determineScopeFor->code (id) {
 +
    # where id is the id of a role object
 +
   
 +
    # NOTE: NonBusinessIdSystems is a list of OIDs for all Id systems that are known NOT to be BUSN
 +
    # Such a list will always be context/prject specific
 +
   
 +
    if (id/@root in  NonBusinessIdSystems) { return "OBJ" } else { return "BUSN" }
 +
}
 +
&nbsp;
 +
&nbsp;
 +
Function usesAppellation->boolean (code) {
 +
    # where X is an object of type entity
 +
 +
    # NOTE: AppellationCodeSystems is a list of OIDs for all coding systems that are known
 +
    # to be appellations. Such a list will always be context/prject specific
 +
   
 +
    if (code/@codeSystem in AppellationCodeSystems) { return "true" } else { return "false" }
 +
}

Latest revision as of 05:37, 25 April 2015


FHIR Notes

We'll probably need some guidance as to how to determine 'Resource Equivalence', especially given that all componehts of the Identifier datatype are optional. Does the scoper still play a role in the context of some resources, such as Patient ?

Overview

The Object Identity (a.k.a. Business Identity, identity throughout the business cycle) of an object uniquely identifies the object. Two objects (e.g. as present in two different messages) with the same Object Identity are one and the same object.

Identity and Equality

Definitions:

  • Identical: Two objects are identical if all their properties are equal, and they cannot be distinguished
  • Equal: Two objects are equal if their identifying characteristics are equal.
  • Equivalent: Two objects are equivalent if their properties are different, but it is known that they refer to the same real world concept

Scope of Identifier

An identifier (the identifying characteristic, if expressed as an II data type in HL7) can be of different types (have different characteristics). The Datatpes R2 specification documents the II.scope (CS datatype) attribute, which is defined as Specifies the scope in which the identifier applies to the object with which it is associated..

  • The CodeSystem used is "IdentifierScope", with OID: 2.16.840.1.113883.5.1116. Values are:
    • BUSN: Business Identifier. An identifier whose scope is defined by business practices associated with the object. In contrast to the other scope identifiers, the scope of the use of the id is not necessarily restricted to a single object, but may be reused for other objects closely associated with the object due to business practice.
    • OBJ: Object Identifier. The identifier associated with a particular object. It remains consistent as the object undergoes state transitions.
    • VER: Version Identifier. An identifier that references a particular object as it existed at a given point in time. The identifier SHALL change with each state transition on the object. I.e. The version identifier of an object prior to a 'suspend' state transition is distinct from the identifier of the object after the state transition. Each version identifier can be tied to exactly one ControlAct event which brought that version into being (though the control act may never be instantiated). NOTE: Applications that do not support versioning of objects must ignore and not persist these ids to avoid confusion resulting from leaving the same identifier on an object that undergoes changes.
    • VW: View Specific Identifier. An identifier for a particular snapshot of a version of the object. This identifies a view of the business object at a particular point in time, and as such identifies a set of data items that can be digitally signed and/or attested. This is in contrast to the Version Identifier which identifies the object at a specific time, but not the amount of information being asserted about the object. This identifier would be changed when a transformation of the information is performed (eg to add code translations, to provide a simplified textual rendering, or to provide additional information about the object as it existed at the specific point in time).

Guidance, to be added to Core Principles

The rules for determining when matching identifiers can lead to certainty about equivalence of the objects being compared are unclear.

The following guidance was developed during an MnM meeting in Sydney (20110111, Q3), 
this guidance will be added to a future version of the Core Principles document.

Role equivalence

Role identifiers can be considered to refer to identical role objects IFF:

  • There are II values on each of the roles that are equivalent according to the II equivalence definition
  • Both IIs declare the scope property
  • Both scope properties are one of OBJ, VER or VIEW (they need not be the same on both instances)

In all other circumstances, object equivalence cannot be safely assumed without specific knowledge of the implementation environment, no matter how many other attributes are compared. (It may still be possible to achieve "high probability matches" with less information.)

Example: Clinic uses Social Security Number as their "public" patient identifiers plus an internal object identifier. Patient comes to clinic for several years, then leaves for several years and the role gets flagged as "terminated". They then come back to the clinic and a new role is created for the patient, still using the same SSN as the "public identifier, but with a new internal object identifier. Eventually they leave again and the status becomes terminated again. The SSN cannot be used to point to a single role object instance, even when paired with classCode, code, status, scoper, etc.

Playing Entities of Roles with equivalent Role identifiers can be considered to be equivalent.

Entity equivalence

The same rules for determining equivalency apply as for roles (equivalent IIs each with declared scopes other than BUSN).

In addition, in some cases an entity may be uniquely identified by the code. This may occur for entities with determinerCode of INSTANCE (e.g. countries) as well as those with determinerCode of KIND (e.g. drugs). This equivalence cannot be safely asserted without specific knowledge of the implementation environment.

For example, the ISO country codes might be used to "identify" both country codes (geographic region) and nation codes (political organization). However, the geographic entity object of type X is not equivalent to the nation entity object of type X.

Note: If an Entity II does not have a declared scope, in most circumstances it will be reasonable to presume it is of type "OBJ" and can be treated accordingly.

Act equivalence

The same rules for determining equivalency apply on Acts as for Roles (equivalent IIs each with declared scopes other than BUSN).

Note: If an Act II does not have a declared scope, in most circumstances it will be reasonable to presume it is of type "OBJ" and can be treated accordingly.

Participation and RoleLink equivalence

The scope property for IIs on Participations and RoleLinks must never be BUSN or VIEW. As such, the IIs can always be presumed to be comparable. The sole equivalence requirement is that the objects have equivalent IIs.

Additional guidance for software implementers (to be updated)

The wording in the above section applies at the universal level without one being aware of the context/setting of where the HL7 v3 objects are being used.

The following also applies at the universal leve:

  • The identity of the Role depends on the identity of the playing and scoping entities; those identities may not be known.
    • If the playing entities are both present but not equivalent, the roles being played by these entities are (by definition) not equivalent.
    • It's a business rule if changing the scoper requires a new role, and therefore the identity of the scoper can't be used to determine equivalence of roles.

Equivalence can only really be determined if one knows the value II.scope. HL7 v3 implementers (RIMBAA implementers) that use Datatypes R1 should pre-adopt II.scope, i.e. support that concept in their application even if the wire format doesn't. In most circumstances (though not universally so, see below) the value of II.scope can be determined by looking at II.root. See section below for details.

On occasion Entities may be identified through Entity.code instead of Entity.id. If the concept codes in a coding system identifies a single object (e.g. then this is known as appellation (according to ISO 704). In most circumstances (though not universally so, see below) the value of C?.codingSystem (an OID) can be used to determine if it acts as an identifier. See section below for details.

There are implementations where Role identifiers are 'pushed into' the entity; the role object itself doesn't have a populated id. One can reasonably assume the role.id to be the same as the entity.id.

If the Role.id of at least one Role object has an unknown II.scope, and both IIs are equivalent, then two Role objects can only be said to be identical if: scoping entities are identical AND playing entities are identical AND code/classCode of the Role objects are the same, THEN one could reasonably conclude that two Role objects are the same.

All RIMBAA applications, in order to decide if two roles are the equivalent will have to build some kind of "role identity management component".

Models may contain 'Act References', 'Role References'. These objects are rather minimalistic in nature, they contain an ID, no code attribute, and a generic classCode/moodCode. In this situation the ID really has to be a single Act instance (OBJ, VER, VIEW). If the ID is a business identifier the reference object won't be very useful.

II.root and II.scope

It is tempting to look at the root OID, and if it is equal to (for example) the OID of the US SSN identification scheme (a widely used identifier for all sorts of roles), to state that the II.scope is therefore equal to BUSN.

At the universal level this however isn't true. For the above example it depends on the scoper of the Role: if the scoper is the US Governement (who assigns SSNs), then the II.scope will be OBJ (the US Governement uniquely identifies one single citizen by means of an SSN). If the scoper is a hospital, the use of SSN is as a BUSN identifier. There are also circumstances where (e.g. for Acts) one systems OBJ scoped II is another systems VER scoped II.

Whilst one may not be able to deduce the value of II.scope from II.root at the universal level it is very likely that one is able to do so within a single project/context. Within a specific context one will be aware if (for example) the scoper of a role identified with an SSN will ever be the US Government. If not, then the SSN root OID implies that the scope of the identifier is BUSN.

Entity.code and appellations

It is tempting to look at the OID of the coding system, and if it is equal to (for example) the OID of the ISO Country coding system, to state that the code is an appellation (i.e. that the code is an identifier with scope OBJ).

At the universal level this however isn't true. For example, the ISO country codes might be used to "identify" both Country entities (geographic region) as well as Nation entities (political organization). However, the geographic entity object of type X is not equivalent to the nation entity object of type X.

Whilst one may not be able to deduce whether or not a code is an appellation at the universal level it is very likely that one is able to do so within a single project/context. Within a specific context one will be aware that (for example) Country entities are never used. In that case the codeSystem OID for the ISO Country coding system implies that the code always has the appellation characteristic.

Pseudo code

The pseudo code below captures the recommendations as shown above.

# A and B are both objects of any RIM class. How do we determine equivalence?

Function ObjectsEquivalent->boolean (A,B) {

   #----- Phase 1: fudge missing object.id values if possible
   if isEntity(A) and not populated(A.id) and populated(A.code) {
      #code could be an appellation
      if usesAppellation(A.code) { 
           set A.id/@root = A.code/@codeSystem
           set A.id/@extension = A.code/@code
           set A.id/@scope = "OBJ"
      }
   }
   if isEntity(B) and not populated(B.id) and populated(B.code) {
      #code could be an appellation
      if usesAppellation(B.code) { 
            set B.id/@root = B.code/@codeSystem
            set B.id/@extension = B.code/@code
            set B.id/@scope = "OBJ"
      } 
   }
   if isRole(A) and not populated(A.id) { set A.id = A.playingEntity.id }
   if isRole(B) and not populated(A.id) { set A.id = A.playingEntity.id }
   
   #----- Phase 2: determine equivalence based on the IIs of the two objects
   if populated(A.id) and populated(B.id) {
   
       if (A.id/@root = B.id/@root) and (A.id/@extension = B.id/@extension) {
            # II equivalence
            if DataypesR2 and (A.id/@scope = B.id/@scope) and not (B.id/@scope = BUSN) {
               # stringest form of equivalency, jniversally true
               return "true"
            } else {
               # II.scope only matters for Role.id
               # for RoleLinks and Participations scope by definition will never be BUSN 
               # for Acts and Entities one can rasonably assume it not to be a BUSN identifier 
               if not isRole(A) { 
                  return "true" 
               } else { 
                   # fudge/kludge missing id/@scope values (if any) -
                   # may not be possible, may not be reliable
                   if not populated (A.id/@scope) { set A.id/@scope = determineScopeFor(A.id) }
                   if not populated (B.id/@scope) { set B.id/@scope = determineScopeFor(B.id) }
                   # for Role equivalence, scope has to be equal, but not BUSN
                   if (A.id/@scope = B.id/@scope) and not (A.id/@scope = BUSN) { 
                      return "true" 
                   } else { 
                      return "false" 
                   }
               }
            }
       } else {
          return "false"
       }
   
   } else {
     # at least one id is missing, one or both ids not populated
     return "false"
   }

} 
 
 
Function determineScopeFor->code (id) {
   # where id is the id of a role object
   
   # NOTE: NonBusinessIdSystems is a list of OIDs for all Id systems that are known NOT to be BUSN
   # Such a list will always be context/prject specific
   
   if (id/@root in  NonBusinessIdSystems) { return "OBJ" } else { return "BUSN" }
}
 
 
Function usesAppellation->boolean (code) {
   # where X is an object of type entity

   # NOTE: AppellationCodeSystems is a list of OIDs for all coding systems that are known 
   # to be appellations. Such a list will always be context/prject specific
   
   if (code/@codeSystem in  AppellationCodeSystems) { return "true" } else { return "false" }
}