<rss version="2.0">
  <channel>
    <title>Hiperspace</title>
    <link>https://www.cepheis.com/hiperspace/blog</link>
    <description><![CDATA[Documentation for Hiperspace ]]></description>
    <item>
      <title>Release 2nd August 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260802</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release enhances the support for alternate indexes to include nested values and handling of unique references to other entities.</p>
<p>This enhancement removes the need to define additional element values to support view access.</p>
<hr>
<h2>Alternate Index</h2>
<p>The normal/<strong>best</strong> way to define an index is to include an access path.  The following <em>model</em> defines a Customer and Account <em><strong>and</strong></em> implicitly an alternate index for navigation from Customer to all accounts.</p>
<pre><code>entity Customer (Id : Int32){Name : String}[Accounts : Account (Customer = this)];
entity Account (Id : Int32){Customer : Customer};
</code></pre>
<p>The <code>@AlternateIndex</code> property of a <em>key</em>/<em>value</em> allows for indexes to be created where it is not possible to define an access path (e.g. to <code>segment</code> or <code>aspect</code> that may have multiple implementations.  If Account is instead defined as a <em>segment</em>, it would not be possible to define an access path from <code>Book</code> to <code>Account</code> since the segment could be defined for any number of <em>elements</em>.</p>
<pre><code>entity Customer = Node ()(Id : Int32) {Name : String} [Accounts : Account, TypeName = "Customer"];
segment Account = Node(), Edge() (Id : Int32) {Name : String, @AlternateIndex Book : Book} [TypeName = "Account", From = Book, To = this];
entity Book = Node() (Id : Int32) {Name : String}[TypeName = "Book"];
</code></pre>
<p>When <code>Book</code> is viewed as a <em>graph</em> <code>Node</code>, it has a <code>Froms</code> collection of <code>Edge</code> that includes all <em>Accounts</em> that refer to the <code>Book</code>.  <code>@AlternateIndex</code> creates an access path to <code>Account</code> from <code>Book</code>.</p>
<h3>ISO20022</h3>
<p><a href="https://github.com/channell/Hiperspace.ISO20022/blob/291d6bb1f03bc6925d31b702ccc53986a4217903/Static.hilang#L498">ISO20022.Static.BranchAndFinancialInstitution</a> includes a nested value <code>ISO20022.Static.FinancialInstitution</code> that could be a <em>Bank</em>, a <em>LegalEntity</em> or both.</p>
<pre><code>entity ISO20022.Static.BranchAndFinancialInstitution 
    = Node (Name = FinInstnId.Nm, SKey = SKey, TypeName = "ISO20022.BranchAndFinancialInstitution")
    , ISO20022.Edges 
        ( From = this
        , To = Bank
        , Name = BankName
        , FromTypeName = "BranchAndFinancialInstitution-Bank"
        , ToTypeName =  "Bank-BranchAndFinancialInstitution"
        )
(
    @XmlElement BrnchId            : ISO20022.Static.BranchData,
    @XmlElement FinInstnId         : ISO20022.Static.FinancialInstitution,
)
[
    @XmlIgnore, JsonIgnore 
    Bank                           : ISO20022.Static.Bank (AnyBIC = FinInstnId.BICFI),
]		
</code></pre>
<p>This release adds the option to include <code>@AlternateIndex</code> for <code>ISO20022.Static.FinancialInstitution.BICFI</code> and have an index on every <em>entity/segment/aspect</em> that includes the <code>FinancialInstitution</code> reference.  When <code>ISO20022.Static.Bank</code> is viewed as a graph <code>Node</code> it will include a <em>edges</em> to and from every <code>BranchAndFinancialInstitution</code> that referenes it.</p>
<hr>
<h2>Unique Reference</h2>
<p>When an <em>element</em> has an extent reference to another <em>element</em> it is normally a <strong>set</strong>, in the top example <code>Customer.Accounts</code> is a set because any number of <code>Account</code> could refer to <code>Customer</code>, but if the parameter to the reference is the same as key of the referenced <em>element</em> it is included as <strong>single</strong> since it will never have more than one value. When an <em>element</em> includes a reference to an <code>aspect</code> (single value inheriting the owner’s key) the <strong>single</strong> value behaves like a nested value:</p>
<ul>
<li>Reading the <code>Value</code> does a lazy lookup from the <code>SubSpace</code> cache, or from the underlying <code>HiperSpace</code></li>
<li>Setting the <code>Value</code> copies the <em>owner</em> key to the aspect transparently to behave like a value but stored seperately.</li>
</ul>
<p>This release add the ability set foreign references in the same way as <code>aspect</code>, but instead of binding the <em>owner</em> of the aspect, the parameter fields in the referencing element are updated.</p>
<p>For the ISO20022 example above the code <code>var aBranch = new BranchAndFinancialInstitution { Bank = goldmanSachs };</code> will assign <code>goldmanSachs.AnyBIC</code> value to <code>aBranch.FinInstnId.BICFI</code> since that is semantically equvilent to <code>var aBranch = new BranchAndFinancialInstitution { FinInstnId = new ISO20022.Static.FinancialInstitution { BICFI = goldmanSachs }};</code></p>
<hr>
<h1>Worked Example</h1>
<p>The <a href="https://github.com/channell/Hiperspace.ISO20022/blob/main/Static.hilang">ISO20022</a> includes the defintion of <strong>Party</strong> that can <em>optionally</em> reference <em>a Legal Entity</em>, <em>a Bank</em> or <em>both</em>, in addtion to generic <em>person</em> and <em>branch</em> information.  The entity can be <em>viewed</em> as a <code>Node</code> in graph (<em>with the <code>TypeName</code> "ISO20022.Party"</em>) <strong>and</strong> up to four <code>Edge</code>:</p>
<ul>
<li><strong>Party-Bank</strong> (<em>if <code>OrgId.AnyBIC</code> has a value</em>) - via Party Key</li>
<li><strong>Bank-Party</strong> (<em>if <code>OrgId.AnyBIC</code> has a value</em>) - via Party AnyBIC index</li>
<li><strong>Party-LegalEntity</strong> (<em>if <code>OrgId.LEI</code> has a value</em>) - via Party Key</li>
<li><strong>LegalEntity-Party</strong> (<em>if <code>OrgId.LEI</code> has a value</em>) - via Party LEI index</li>
</ul>
<p>if we query <code>SELECT node, edge.To AS Party FROM Nodes AS node, node.Tos /*implicit join*/ AS edge WHERE node.Name = 'Goldman Sachs' AND edge.TypeName = 'Bank-Party';</code> will return a result set of all <em>parties</em> that participate in an ISO20022 message for the Bank with the name Goldman Sachs. The query plan will perform:</p>
<ol>
<li>Create a template <code>Node</code> with <code>Name</code> "Goldman Sachs"</li>
<li>Query  <code>SetSpace&lt;Node&gt;</code> for all <code>Node</code> that has a index on Name (<em>including <code>node.TypeName = 'ISO20022.Bank'</code> would limit the query to <code>SetSpace&lt;Bank&gt;</code></em>)</li>
<li>Create a template <code>Bank</code> with  <code>Name</code> "Goldman Sachs"</li>
<li>Query <code>SetSpace&lt;Bank&gt;</code> using the Bank Name index</li>
<li>Create a template <code>Edge</code> with <code>From</code> set to the <code>Node</code> object for "Goldman Sachs" and TypeName 'Bank-Party'</li>
<li>Query <code>SetSpace&lt;edge&gt;</code> (<em>since edge TypeName is set, only <code>SetSpace&lt;Party&gt;</code> is searched</em>)</li>
<li>Create a template <code>Party</code> with <code>Bank</code> set to "Goldman Sachs" (<em>the <code>binder</code> code adds an <code>OrgId</code> value, and set <code>OrgId.AnyBIC</code> to <code>Bank.AnyBIC</code></em>)</li>
<li>Query <code>SetSpace&lt;Party&gt;</code> using the Party AnyBIC index</li>
<li>Project a data frame of Node and Party</li>
</ol>
<p>The following LINQ query will traverse the graph of nodes through all edges (upto a length 100) to find CreditTransferTransaction associated with "Goldman Sachs":</p>
<pre><code>from node in space.Nodes 
where node.Name == "Goldman Sachs" 
let transfer = node.HiperEdges("*", 100, new HashSet&lt;string&gt; {"Fact:ISO20022.Cube.CreditTransferTransaction"}
select transfer;
</code></pre>
<p>For <strong>Hiperspace.DB</strong> the graph search will use GPU parallel search, otherwise a parallel pipeline is used.</p>
<hr>
<h2>Referenced libraries</h2>
<p>External references have been updated to the latest version</p>
]]></description>
      <pubDate>Sun, 02 Aug 2026 15:39:39 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260802</guid>
    </item>
    <item>
      <title>Release 20th July 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260720</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is a minor update for XML and JSON serialisation, following testing with  <a href="https://www.iso20022.org/">ISO20022</a>.  XML Serialisation (with dotnet) reflects its age and is does not support references for elements referenced more than once in an XML document.</p>
<p>This release adds anotations to Hiperspace and HiLang to exclude Stringified key (<code>SKey</code>) and <code>owner</code> references from XML and JSON serialisation.</p>
<hr>
<h2>Referenced libraries</h2>
<p>External references have been updated to the latest version</p>
]]></description>
      <pubDate>Mon, 20 Jul 2026 22:27:22 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260720</guid>
    </item>
    <item>
      <title>Release 6th July 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260706</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is a significantly enhances support for <a href="https://www.w3.org/XML/Schema">XML Schema</a>, an introduces generated mapping for <a href="https://www.iso20022.org/">ISO20022</a> messages.</p>
<p>ISO20022 includes a large family of 761 XSD schema files comprising 39954 separate type definitions (<em>many of which are duplicated in each schema file, but some specific domain variations</em>).
As a <em>proof-of-principle</em> the message schema has been mapped <em>as-is</em> with payments messages augmented to show how a <strong>Graph</strong> and <strong>Cube</strong> functionality can be added with minimal changes to store messages in hiperspace.  <em>Messages</em> stored in hiperspace are <em>naturally</em> compressed from the XML or JSON source using protobuf encoding that replaces type names with the equivalent  <strong>#id</strong> and native types instead of text encoding.</p>
<h2>Hilangc changes</h2>
<p>Import of XSD schema has been added to the command-line tool</p>
<table>
<thead>
<tr>
<th>Option</th>
<th>┃</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>--xsd</td>
<td>┃</td>
<td>Import the XML schema file (.xsd) to an equivalent hilang model</td>
</tr>
<tr>
<td>--xn</td>
<td>┃</td>
<td>Set the XML schema tag for all imported elements</td>
</tr>
<tr>
<td>--xe</td>
<td>┃</td>
<td>Import references to a list of names as <em>entities</em></td>
</tr>
<tr>
<td>--xs</td>
<td>┃</td>
<td>Import references to a list of names as <em>segments</em></td>
</tr>
<tr>
<td>--xa</td>
<td>┃</td>
<td>Import references to a list of names as <em>aspects</em></td>
</tr>
<tr>
<td>--xt</td>
<td>┃</td>
<td>Import references to a list of names as abstract <em>types</em> - the default is <em>values</em></td>
</tr>
</tbody>
</table>
<h2>Hilang changes</h2>
<p>The  directive <code>%reference (domain.schema.dll);</code>  imports <em>elements</em> from pre-compiled domain schema <a href="https://github.com/channell/Hiperspace.ISO20022/blob/main/all/all.hilang">all.hilang</a> uses the reference import to build a consolidated ISO20022 schema including element definitions from each domain specific models {acmt,	admi,	auth,	caaa,	caad,	caam,	cafc,	cafm,	cafr,	cain,	camt,	canm,	casp,	casr,	catm,	catp,	colr,	fxtr,	head,	pacs,	pain,	reda,	remt,	secl,	seev,	semt,	sese,	setr,	trck,	tsin,	tsmt,	tsrv}</p>
<p>The directive <code>%annotations;</code> has been added to simplify maintenance of XSD mapping. The code</p>
<pre><code>@XmlNameSpace ("urn:iso:std:iso:20022:tech:xsd:pain.001.001.12"), ValidationCheck
entity ISO20022.Pain001001.CustomerCreditTransferInitiationV12 #35044
(
    @XmlElement GrpHdr             : ISO20022.Pain001001.GroupHeader114 #1,
)
{
    @XmlElement SplmtryData        : List&lt;ISO20022.Pain001001.SupplementaryData1&gt; #2,
};
</code></pre>
<p>could be modeled as</p>
<pre><code>@XmlNameSpace ("urn:iso:std:iso:20022:tech:xsd:pain.001.001.12"), ValidationCheck
entity ISO20022.Pain001001.CustomerCreditTransferInitiationV12 #35044
{
    @Key, XmlElement GrpHdr             : ISO20022.Pain001001.GroupHeader114 #1,
    @XmlElement SplmtryData             : List&lt;ISO20022.Pain001001.SupplementaryData1&gt; #2,
};
</code></pre>
<p>to transform the the <code>GrpHdr</code> element of <code>ISO20022.Pain001001.CustomerCreditTransferInitiationV12</code> from a <em>value</em> to a <em>key</em> without reordering the memvbers of the element.<br>
This is intended to simplify the tracking of a golden-source of model using code change-control tools</p>
<h2>Hiperspace changes</h2>
<p>Various Hiperspace structures have been changed from <code>struct</code> to <code>class</code> to avoid stack-space issues with huge models.
<code>netstandard2.0</code> has been added as a target for Hiperspace assembly to enable meta-data definitions to be read by Hilang during compilation (or generation using <em>hilangc</em>)</p>
<p>Interface functions that were marked as Obsolete in earlier releases have been removed.</p>
<p>Additional Transactional versions of the <code>Bind</code>,  <code>Get</code> and <code>Find</code> functions have been added, initially using the old functions. These functions take a <strong>MVCC Transaction</strong> reference has an additional parameter and a <a href="https://github.com/channell/Hiperspace/blob/master/src/Hiperspace/DurableType.cs">DurableType</a> to allow the functions to be used for:</p>
<table>
<thead>
<tr>
<th>Type</th>
<th>┃</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Elements</td>
<td>┃</td>
<td>Entities, Segments, Aspects</td>
</tr>
<tr>
<td>Versioned</td>
<td>┃</td>
<td>Versioned Entities, Segments, Aspects</td>
</tr>
<tr>
<td>MetaModel</td>
<td>┃</td>
<td>dictionary that ensures that Hiperspaces cannot be used by incompatible clients</td>
</tr>
<tr>
<td>VectorSpace</td>
<td>┃</td>
<td>VectorSpace token lists for geospatial and nearest neighbor search</td>
</tr>
<tr>
<td>Sequences</td>
<td>┃</td>
<td>Sequence numbers</td>
</tr>
<tr>
<td>Transaction</td>
<td>┃</td>
<td>{start, end, reference} elements for MVCC</td>
</tr>
<tr>
<td><strong>MetaMap</strong></td>
<td>┃</td>
<td>protobuf transformation structure for Meta-Model compliant serialisation of elements</td>
</tr>
<tr>
<td><strong>Fingerprint</strong></td>
<td>┃</td>
<td>To avoid re-generation of meta-map when Hiperspace has been reopened by a pre-validated client</td>
</tr>
</tbody>
</table>
<p>Hiperspace <code>Find</code> will search for hiperspace using  the <em>key</em> or <em>index</em> values.  The new function extends the search parameters to include a collection of values that are not part of a <em>key/index</em> to reduce the number of elements returned from hiperspace to pre-filter elements close to source.</p>
]]></description>
      <pubDate>Mon, 06 Jul 2026 16:42:20 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260706</guid>
    </item>
    <item>
      <title>Release 9th May 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260509</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is a minor update to support the presentation Cube data in  Graph views with <strong>Hiperspace.DB</strong>, and minor updated to external references.</p>
<h2>Cube enhancement</h2>
<h3>Aggregation</h3>
<p>Hiperspace <strong>Cubes</strong> are declarative and stream-calculated by <code>Hiperspace.DB</code>, and viewable in graphs as a <em>Node</em> with Measures and a <code>TypeName</code> begining with <code>"Cube:"</code> (for aggregates) or <code>"Fact:"</code> for leaves within a <em>Cube</em> hierarchy.  This release changes the presentation of <em>Cube</em> nodes that do not relate to any underlying facts:</p>
<ul>
<li>Do not return a facts collection when the <code>Facts</code> property is zero</li>
<li>Do not return leaf dimensions (to a Pivot-Table) when there are no child dimensions or Measures</li>
</ul>
<h3>Validation</h3>
<p>Validation rules have been relaxed to create <strong>cubes</strong> even if it doesn't have any measures.  <strong>Cubes</strong> provide summary information for a single or multiple <em>Dimensions</em>, but sometimes the only <em>measure</em> you need is the number of connections included:</p>
<pre><code>entity Customer = Node () (Id : Int32) {Name : String} [TypeName = "Customer"];
entity Order  = Node (), Edge (From = Customer, To = this, TypeName= "Order"), Edge_ (From = this, To = Product, TypeName = "Ordered" )
  (Id : Int32) {Customer : Customer, Product : Product} [Name = Customer.Name + "-" + Product.Name, TypeName = "Order"];
entity Product = Node () (Id : Int32) {Name : String} [TypeName = "Product"];
</code></pre>
<p><img src="/hiperspace/media/Sites/hiperspace/cube/spread.svg"></p>
<p>If we're not interested in each order, a <strong>Cube</strong>  can be used to simplify the graph to</p>
<p><img src="/hiperspace/media/Sites/hiperspace/cube/summary1.svg"></p>
<p>by changing the model to</p>
<pre><code>@CubeDimension entity Customer = Node () (Id : Int32) {Name : String} [TypeName = "Customer"];
@CubeFact entity Order (Id : Int32) {Customer : Customer, Product : Product};
@CubeDimension entity Product = Node () (Id : Int32) {Name : String} [TypeName = "Product"];
</code></pre>
<p>If any of the <em>dimensions</em> to a <strong>Cube</strong> can be viewed as a <em>Node</em>, the <code>_Cube</code> and <code>_Fact</code> elements become a <code>Node</code> with <em>Drilldown</em> <code>Edge</code> to each dimension.
This change enables <strong>Cube</strong> to be used to simplify a <em>graph</em>, even when the only thing being measured is the number edges being replaced.</p>
<h3>View enhancement</h3>
<p>The addition of the Cube summaries enhances the presentation of Graphs but introduced a level of detail that might be a distration for analysis.  If you have a trade <em>cube</em> with dimensions {Sector, Product, Country} and drilldown to the lowest level of detail, a Graph search will include each level of detail all with <em>node</em> <code>"Cube:Contract_Cube"</code> and <em>edges</em> <code>"Cube:Drilldown"</code>  and <code>"Cube:Dimension"</code>.  Too enable more targeted analysis, <em>node</em> <code>TypeName</code> has been enhanced to include the number of dimensions included in each cube:</p>
<ul>
<li> Cube has TypeName <code>"Cube:Contract_Cube:1"</code>  with Sector-&gt;Cube (TypeName <code>"Cube:Drilldown:1"</code></li>
<li>{Sector, Product} Cube has TypeName <code>"Cube:Contract_Cube:2"</code> with Sector-&gt;Cube (TypeName <code>"Cube:Drilldown:2"</code></li>
<li>{Sector, Product, Country} Cube has TypeName <code>"Cube:Contract_Cube:3"</code> with Sector-&gt;Cube (TypeName <code>"Cube:Drilldown:3"</code></li>
</ul>
<hr>
<h2>Element enhancement</h2>
<p>The <code>IElement</code> interface of <code>Element&lt;TElement&gt;</code> has been updated to include an <code>ISetSpace</code> reference for the trnasformation of <code>ICubeFact</code> <em>facts</em> to a acyclic directed graph needed to render <strong>Cubes</strong> as a <em>Pivot-Table</em> with <em>drilldown</em> to futher details.</p>
<h2>HiLang enhancement</h2>
<p>Extent expressions do not <em>normally</em> need <code>#id</code> to be recorded because they are not stored (calculated when needed from an element), but this is not the case for cube measures.  Source editing has been added to carry forward the same <code>id</code>  as the model changes.</p>
<hr>
<p>Release also includes updates to referenced libraries</p>
]]></description>
      <pubDate>Sat, 09 May 2026 21:23:31 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260509</guid>
    </item>
    <item>
      <title>Release 24th April 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260424</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is a minor update to support the presentation Cube data in  Graph views with <strong>Hiperspace.DB</strong>, and minor updated to external references.</p>
<h2>Fact enhancement</h2>
<p>A specific <code>ICubeFact&lt;TFact&gt;</code> inteface has been added to include <code>public IEnumerable&lt;TFact&gt; DrillDown(Type dimension);</code> for generic drilldown from one Cube to the next level when viewed in <strong>Hiperspace.DB</strong>.</p>
<p>The <code>ICubeDimension</code> interface now has a <code>GetCube()</code> function to get the named <code>ICubeFact</code> for use with a <strong>PivotTable</strong> view of the <em>Cube</em> through <strong>Hiperspace.DB</strong>.</p>
<h2>SubSpace enhancement</h2>
<p><code>SubSpaceParameters</code> has been enhanced to include <code>TransactionalPolicy</code></p>
<table>
<thead>
<tr>
<th>┃</th>
<th>Policy</th>
<th>┃</th>
<th>Notes</th>
<th>┃</th>
</tr>
</thead>
<tbody>
<tr>
<td>┃</td>
<td>NotTransactional</td>
<td>┃</td>
<td>Indicates that the Space is not transactional</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>Transactional</td>
<td>┃</td>
<td>Specifies that a new transaction should be started</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>ParentTransaction</td>
<td>┃</td>
<td>Indicates that the space participates in the transaction of its parent space</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>ContinueTransaction</td>
<td>┃</td>
<td>Transaction is a continuation of a prior (long-running) transaction</td>
<td>┃</td>
</tr>
</tbody>
</table>
<hr>
<h2>Hilang enhancement</h2>
<p>Added support support for Cube aggregation of views in addition to {<em>entity, segment, aspect</em>}.</p>
<p>Consider the model where there are seperate <em>entitites</em> for {<em>FX, FI, EQ</em>} Trades that have different values for Entities, Bonds, Foreign Exchange, but can all be <em>viewed</em> as a Trade that has a booking_id, and a valuation (NPV).  This change adds support for <strong>Cube</strong> analytics of <em>views</em>  combining all trades in all entities that provide the view.</p>
<pre><code>type Banking.TradeBase
(
	Id : String
) 
{
	Book : Banking.Book,
	@CubeMeasure(Aggregate.Sum)
	NPV : Decimal
};

@CubeFact
view Banking.Trade : Banking.TradeBase;

entity Banking.FI.Trade : Banking.TradeBase = Banking.Trade();
entity Banking.FX.Trade : Banking.TradeBase = Banking.Trade();
entity Banking.EQ.Trade : Banking.TradeBase = Banking.Trade();

@CubeDimension
entity Banking.Book ( Id : String ) [ Trades : Banking.Trade (Book = this)];
</code></pre>
<p>When the schema is compiled with <a href="https://www.nuget.org/packages/HiLang">HiLang</a>, <code>Banking.Book</code> has an additional property <code>Trade_Cube</code>  that aggregates NPV over the full set trades that can be <em>viewed</em> as a <code>Banking.Trade</code>.</p>
<h3>Enhanced List support</h3>
<p>Keys in a hilang element can be:</p>
<ul>
<li>A Value including value elements defined in Hilang and primatives (e.g. String, Int32, Guid, etc)</li>
<li>A Reference to an entity (only the Key-part of the referened entity is serialised)</li>
<li>A List of Values (e.g. List&lt;String&gt;) but not a Set&lt;&gt; since a Set order can change</li>
</ul>
<p>Key support for <code>List&lt;&gt;</code> in keys has been extended to include Keys of Lists of Values of Lists of Values, that can be placed in a <code>SortedSet</code>. 	Examples include <a href="https://www.finos.org/common-domain-model">FINIOS CDM</a> that supports complex identifiers that include list of identifiers</p>
<pre><code>value CDM.PartyIdentifier ( identifier : String );
entity CDM.Party ( partyId : CDM.PartyIdentifier );
value CDM.PriceQuantity;
value CDM.AssetIdentifier
(
    identifer : String,
    version : Int32
);
value CDM.Identifer
(
    assignedIdentifier : List&lt;CDM.AssetIdentifier&gt;,
    issuerReference : CDM.Party,
    issuer : String
);
entity CDM.TradeLot
(
    lotIdentifier : List&lt;CDM.Identifer&gt;
)
{
    priceQuantity : List&lt;CDM.PriceQuantity&gt;
};
</code></pre>
<p>In this example <code>CDM.TradeLot</code> has a key that is a 	<code>List&lt;CDM.Identifer&gt;</code> which itself contains a <code>List&lt;CDM.AssetIdentifier&gt;</code>.  Lists are supported in <a href="https://www.nuget.org/packages/HiLang">HiLang</a> models since the they can be unambiguously serialised to a store and retrived later.  <strong>NB</strong> <code>CDM.Identifer</code> contains a reference to <code>CDM.Party</code> which is a <em>entity</em> so only the <em>key</em> is stored, with <code>CDM.Party</code> retrived only when needed.</p>
]]></description>
      <pubDate>Fri, 01 May 2026 19:22:14 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260424</guid>
    </item>
    <item>
      <title>Release 15th April 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260415</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is a minor update to support the presentation Cube data in  Graph views with <strong>Hiperspace.DB</strong>, and minor updated to external references.</p>
<h2>Fact enhancement</h2>
<p>Every instance of a <code>_Fact</code> or <code>_Cube</code> element now includes a <code>Facts</code> property that excludes <code>Node</code> that is a view of a fact but have no underlying value.</p>
<h2>Node Enhancement</h2>
<p><code>Node</code> now includes a <code>Measures</code> property, which is a <code>List&lt;MeasureValue&gt;</code> to eliminate the need to separately fetch measures when displaying <code>Cube</code> <code>Node</code> in a <strong>Hiperspace.DB</strong> graph display.  Normally a <em>graph</em> showing "<em>which client Sectors trade which products</em>" would involve connecting the edges <em>Sector-&gt;Client-&gt;Account-&gt;Trade-&gt;Leg-&gt;Asset-&gt;Instrument-&gt;Product</em> or <code>HiperEdge</code> <em>Sector=&gt;Trade=&gt;Product</em>, and then aggregate results.  When <em>Sector</em> and <em>Product</em> are <em><code>@CubeDimension</code></em> and <em>Trade</em> is a <em><code>@CubeFact</code></em> the aggregate values are available directly through <em>Sector-&gt;Trade_Cube-&gt;Product</em> with <em>streaming calculation</em> in <strong>Hiperspace.DB</strong>.  Displaying aggregate values in a graph is so useful for analysis that it is worth extending the <code>Node</code> model to include them as an optional property.
<img src="/hiperspace/media/Sites/hiperspace/cube/hipercube.png"></p>
<blockquote>
<p><strong>NB</strong> This diagram is from a <code>HiperGraph</code> of the Sector "All", Product hierarchy aggregates are connected to the Sector hierarchy (without spider connections)</p>
</blockquote>
<h2>Hilang enhancement</h2>
<ul>
<li>Added support for <code>enum</code> values in messages.</li>
<li>Resolved issue with self-referencing messages <code>message Dimension { Branches : List&lt;Dimension&gt; };</code></li>
<li>Name resolution of types now uses global namespace resolution to avoid clashes.</li>
</ul>
<h2>SubSpace enhancement</h2>
<p>Currently Blazer Web Assembly clients cannot (<em>currently</em>) support threads and therefore cannot perform synchronous IO - <code>FindAsync()</code> and <code>GetAsync()</code>  requests can be routed (<em>over <strong>gRpc</strong></em>) to a remote store, <code>Find()</code> and <code>Get()</code> request are therefore <em>cache-local</em>. the test for <code>OperatingSystem.IsBrowser()</code> has been replaced  with a <code>CachePolicy</code> that default to <em><code>CachingPolicy.Cache</code></em> for Browser and <em><code>CachingPolicy.Space</code></em> otherwise.  This allows <code>message</code> to be used to <em>pre-fetch</em> dependencies on the server-side in addition to browser.</p>
]]></description>
      <pubDate>Wed, 15 Apr 2026 21:55:38 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260415</guid>
    </item>
    <item>
      <title>Release 1st April 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260401</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release includes minor enhancement to the graph functionality when used with <strong>Hiperspace.DB</strong> graph view.  <strong>Hiperspace.DB</strong> provides a <em>graph</em> view that aggregates alll databases, so that graph shows all connections both local and foreign.   In the following example <em>Togaf.Has.WorkPackage</em>  includes a value <em>ProjectKey</em> that could be a stringified <code>SKey</code> reference to a project plan in another database.  When the <em>project</em> is viewed as a graph in  <strong>Hiperspace.DB</strong>, the graph includes paths between a <em>project plan</em> and <em>strategic goals</em>.</p>
<pre><code>segment Togaf.Has.WorkPackage : Togaf.Base 
    = Node        ( SKey = SKey, Name = Name, TypeName = "AF-WorkPackage"),
      Edge        (From = owner, To = this, Name = Name, TypeName = "AF-Has-WorkPackage"),
      Togaf.Edge_ (To = owner, From = this, Name = Name, TypeName = "AF-WorkPackage-For"),
      Togaf.Edges (From = this, To = Project, Name = Name, FromTypeName = "AF-WorkPackage-Plan", ToTypeName = "AF-Plan-WorkPackage"),
      Graph.HiperEdge = StrategicEdges #2675 
{
    ProjectKey      : String #29 
}
[
    "All Togaf.Edges that can be projected as Transitative Togaf.Edges to a Business Goal"
    @Once
    StrategicEdges  = StrategicEdge(this),
    Project         = externalnode(ProjectKey)
];
</code></pre>
<p>For efficiency, the <code>SKey</code> property has been changed from a <a href="https://en.wikipedia.org/wiki/Base64">Base64</a> encoding of the <em>key</em> to add the option of a database prefix.  The <code>Node SKey</code> "Togaf.ascxyz==" will only search for matching items "abcxyz==" in the <em>Togaf</em> database.  Within the <em>Togaf</em> database, only the specific segment is searched because the <em>key</em> includes the <code>#Id</code> of the element set.</p>
<p>the following SQL query would return a table of all <em>project plans</em> and <em>Goals</em> that can be reached through the {component, service, function, process, capability, etc} that are in some way associated with the <em>Work Package</em></p>
<pre><code>SELECT project.Name AS "Project Name"
     , goal.Name AS "Goal Name"
     , project.SKey as "Project Id"
     , goal.SKey as "Goal Id"
FROM   Nodes as project
     , project.Tos as planedge
     , planedge.To as workpackage
     , workpackage.Tos as workedge 
     , workedge.To as goal
WHERE  project.TypeName = 'Plan'
  AND  planedge.TypeName = 'AF-Plan-WorkPackage'
  AND  workpackage.TypeName = 'AF-WorkPackage'
  AND  workedge.TypeName = 'Strategic-Goal'
  AND  goal.TypeName = 'AF-Goal';
</code></pre>
<p>NB the five table join uses <em>implicit</em> joins in  <code>Hiperspace.SQL</code> because <code>project.Tos</code> and <code>workpackage.Tos</code> are <code>Set&lt;Edge&gt;</code> properties that are <em>implicitly</em> filtered by the source <code>Node</code></p>
]]></description>
      <pubDate>Wed, 01 Apr 2026 17:09:37 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260401</guid>
    </item>
    <item>
      <title>Release 20th March 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260320</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release introduces two significant enhancements to hyperspace, to support <a href="https://en.wikipedia.org/wiki/Multiversion_concurrency_control">Mult-version concurrency control</a> in <strong>Hiperspace.DB</strong>, and <a href="https://mermaid.js.org/">Mermaid</a> <a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a> documentation for <strong>#AI</strong> agents.</p>
<h2>Multi-Version Concurrency Control (MVCC)</h2>
<p>MVCC is rarely needed for normal Hiperspace transactions because it is possible to use <a href="https://en.wikipedia.org/wiki/Command_Query_Responsibility_Segregation">CQRS</a> to apply a change to <em>elements</em> in Hiperspace that are invisible to other sessions (using <code>Horizon</code> filters), but this is not practical for <strong>Cube</strong> aggregation, where it would be possible to have inconsistent aggregates while updates are applied in real-time.</p>
<p>This implementation of MVCC follows the <a href="https://en.wikipedia.org/wiki/SQL">ISO SQL</a> standard that every database change is enrolled in a transaction and only visible to other sessions when the <em>transaction</em> is committed and backed-out on rollback.  There are no nested transactions, since the implementation (in <strong>Hiperspace.DB</strong>) is efficient and combined with the <code>@DeltaIndex</code> functionality for efficient point-in-time access.</p>
<p>For local <em>hiperspaces</em> (e.g. <a href="https://www.nuget.org/packages/Hiperspace.Rocks">Hiperspace.Rocks</a>) transactions are automatic, and <code>Rollback()</code> will return false</p>
<pre><code>public bool Commit() =&gt; Commit(Transaction);
public bool Rollback() =&gt; Rollback(Transaction);
</code></pre>
<h2>MarkDown documentation with HiLang</h2>
<p>The <a href="https://www.nuget.org/packages/HiLang">HILang</a> <em>schema</em> compiler generates a <em>Domain Specific DataBase</em> that can be used in a client application (including web-assembly) for object persistence either to a local hiperspace (e.g. <a href="https://www.nuget.org/packages/Hiperspace.Rocks">Hiperspace.Rocks</a>) or remotely using <strong>Hiperspace.DB</strong> either with <em>lazy loading</em> of reference or <em>eager loading</em> using <code>message</code>.  Message is analogous to stored procedures that execute on the server (using message <code>key</code>) and return full object <code>value</code> in response using a shared DSDB code.</p>
<p>Code is generated during compilation to produce a DSDB with a number of transformations:</p>
<ul>
<li>Segments (many) are specialized for the Element that references them</li>
<li>Aspects (one) are specialized for the Element that references them</li>
<li><code>@CubeFact</code> elements are transformed adding properties for aggregation</li>
<li><code>@CubeFact</code> elements are transformed to provide <code>_Cube</code> aggregates and <code>_Fact</code> cube cell aggregates</li>
<li><code>@CubeDimension</code> elements are transformed to include <em>DrillDown</em> to aggregates via <em>lazy</em> property</li>
</ul>
<p>These transformations are not visible to <strong>AI Agents</strong> because the generated code is ephemeral (<em>using the Indonesian word <strong>hilang</strong></em>).  For <em>agent-based development</em>, the HiLang schema can be generated to markdown files using the <code>%markdown</code> directive in source.</p>
<h3>Schema.md</h3>
<p>For each <code>.hilang</code> schema file a matching <code>.md</code> file is generated containing formatted documentation in a <strong>AI</strong> friendly form, with a <em>mermaid</em> diagram of the data-model.  This documentation file matches the structure of the <code>.hilang</code> schema</p>
<h3>Schema-physical.md</h3>
<p>For each <code>.hilang</code> schema file a matching <code>.md</code> file is generated containing formatted documentation in a <strong>AI</strong> friendly form, with a <em>mermaid</em> diagram of the data-model.  This documentation file matches the generated classes, including all the transformations referenced above</p>
<h3>Schema-cube.md</h3>
<p>This markdown file contains only <code>_Cube</code> elements and the related <em>dimensions</em></p>
<h3>Schema-graph.md</h3>
<p>This markdown file contains only graph (<code>Node</code> and <code>Edge</code>) elements and a diagram of relations</p>
]]></description>
      <pubDate>Fri, 20 Mar 2026 15:54:39 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260320</guid>
    </item>
    <item>
      <title>Release 27th February 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260227</link>
      <description><![CDATA[<h1>Overview</h1>
<h2>Cube Enhancements</h2>
<p>A minor enhancement has been made to fully qualify references to <strong>Hiperspace.Functions</strong>. This prevents naming conflicts when a domain also uses <strong>Functions</strong>.</p>
<p>When one of a Cube’s <strong>Dimensions</strong> is <code>Node</code>, the <code>Node</code> view is added to the Cube along with <code>Edge</code> definitions that represent navigation between the <strong>Dimension</strong> and the Cube. In this release, the naming of these edges has been updated to ensure consistent navigation through a HiperGraph, using a common TypeName convention:</p>
<ul>
<li>TypeName <code>Cube:Drilldown</code> for navigation from a  <em>Dimension</em> to the <em>Cube</em>.</li>
<li>TypeName <code>Cube:Dimension</code> for navigation from the <em>Cube</em> to a <em>Dimension</em>.</li>
</ul>
<h3>Example</h3>
<p>In the <a href="https://github.com/channell/Hiperspace/blob/master/examples/Cube/Cube.hilang">Cube Example</a> <em><strong>Customer Sector</strong></em> and <em><strong>Product</strong></em> are <em>Dimensions</em> that can also be viewed as <code>Nodes</code>. The <em><strong>Contract Cube</strong></em> therefore has <code>Node</code> and <code>Edges</code> added as a views because.</p>
<p><img src="/hiperspace/media/blogs/Blogs/Butterfly/product-model.png"></p>
<ul>
<li><em><strong>Contact</strong></em> (<em>Cube</em>) has one <em><strong>Account</strong></em> and <em><strong>Account</strong></em> has one <em><strong>Customer</strong></em> and <em><strong>Customer</strong></em> has one <em><strong>Sector</strong></em> (<em>Dimension</em>), it is inferred that  <em><strong>Sector</strong></em> is a <em>Dimension</em> of <em>Cube</em> <em><strong>Contact</strong></em>.</li>
<li><em><strong>Contact</strong></em> (<em>Cube</em>) has one <em><strong>Instrument</strong></em> and <em><strong>Instrument</strong></em> has one <em><strong>Product</strong></em>, it is inferred that <em><strong>Product</strong></em> is a <em>Dimension</em> of <em>Cube</em> <em><strong>Contract</strong></em>.</li>
</ul>
<p>When viewed through a <strong>HiperGraph</strong> the <em>Cube</em> becomes a <code>Node</code> that links <em><strong>Sector</strong></em> <em>node</em> to <em><strong>Product</strong></em> <em>Node</em></p>
<p>If you <em>drilldown</em> from <em><strong>Product</strong></em> to <em><strong>Contact_Cube</strong></em> and <em>drilldown</em> to <em><strong>Sector</strong></em>, the shortest <em>HiperEdge</em> from <em><strong>Product</strong></em> to <em><strong>Sector</strong></em> is through the <em><strong>Contract_Cube</strong></em> slice that summarizes the connection between the two <code>Nodes</code>.  We call this combination of <em>HiperGraph</em> and <em>Cube</em> "<em>HiperCube</em>", and is useful for <strong>AI</strong> prompts like <em>"which sector uses most widgets?"</em></p>
<p><img src="/hiperspace/media/blogs/Blogs/Butterfly/product-graph.png"></p>
]]></description>
      <pubDate>Fri, 27 Feb 2026 20:35:06 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260227</guid>
    </item>
    <item>
      <title>Release 14th February 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260214</link>
      <description><![CDATA[<h1>Overview</h1>
<h2>Cube enhancements</h2>
<p><code>@CubeDimension</code> and <code>@CubeHierarchy</code> updated to better support generic access to Cube data from Graph Pivot-Table view.</p>
<ul>
<li><code>@CubeDimension</code> name parameter now refers to the property that provides the name of the element in a Pivot-Table dimension.</li>
<li><code>@CubeHierarchy</code> has additional parameters for <em>Children</em> and <em>Level</em></li>
</ul>
<p>Create GPU Accelerated Graph Search introduced a problem for cubes in that every single permutation of aggregate would be evaluated to produce the Graph cache resulting in:</p>
<ul>
<li>A dense MOLAP cube</li>
<li>Expensive invalidation when cube <em>facts</em> change</li>
</ul>
<p>The  <strong>Cube</strong> model has been split into <em>_Fact</em> entity containing just base aggregates projected from <code>@CubeFact</code> elements, and <em>_Cube</em> elements that are calculated <em>on demand</em> for summaries.</p>
<p><em>Drilldown</em> performed well when then was a very small number of dimensions and hierarchy levels, but poorly when the complexity increases.  The mode has been changed from using <em>Drilldown</em> as an index for aggregate calculation  to a scan of matching <em>facts</em></p>
<h3>Is Fact</h3>
<p><code>IsFact</code> attribute of <em>_Cube</em> and <em>_Fact</em> has been removed, as it is no longer needed.  This is a help for BI access of Cube data via ODATA.</p>
<hr>
<h3>Cube Hierarchy</h3>
<p>Keep hierarchy has been enhanced to include the name of the children property in addition to parent and add level to support aggregation of cube summaries</p>
<p>An example dimension is Organization that is structured as a hierarchy with <em>Parent</em> as a reference to the aggregation line, and <em>Children</em> as a collection of organization units below the current node.  When referenced by a <code>@CubeFact</code> element, the aggregate values of all <code>@CubeMeasure</code>s members show the aggregate value of the <em>node</em> and all <em>child nodes</em></p>
<pre><code>@CubeDimension, CubeHierarchy(Parent, Children, Tier)
entity Organization (Name : String) {Description : String, Parent : Organization, Tier : Int32} [Children : Organization (Parent = this)];
</code></pre>
<p>The generated code implements <code>@CubeHierarchy</code> using the <code>ICubeHierarchy&lt;TEntity&gt;</code> that now includes</p>
<table>
<thead>
<tr>
<th>┃</th>
<th>Name</th>
<th>┃</th>
<th>Notes</th>
<th>┃</th>
</tr>
</thead>
<tbody>
<tr>
<td>┃</td>
<td>Parent</td>
<td>┃</td>
<td>Reference to parent, that must be the same type as the element</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>Children</td>
<td>┃</td>
<td>Collection of child nodes that must be the same type as the element</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>AllPartents()</td>
<td>┃</td>
<td>Function that recursively yields the entire hierarchy upto root node</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>AllChildren()</td>
<td>┃</td>
<td>Function that 	recursively  yields every child node and all their child nodes</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>Level</td>
<td>┃</td>
<td>Level in Hierarchy for <strong>Pivot Table</strong> views that have separate tabs for each level of the hierarchy</td>
<td>┃</td>
</tr>
</tbody>
</table>
<hr>
<h3>Cube Measure</h3>
<p><code>@CubeMeasure</code> has been enhanced include <a href="https://en.wikipedia.org/wiki/Standard_deviation">Population Standard Deviation</a> and <a href="https://en.wikipedia.org/wiki/Percentile">Ranked Percentile</a> aggregates.  <code>Aggregate.StdDev</code> and <code>Aggregate. Percentile</code> use the <code>Vector</code> pseudo-aggregate member that includes every observation in the <em>_Fact</em> element for precise aggregation at the summary level.</p>
<pre><code>@CubeFact, DeltaIndex 
aspect Valuation {@CubeMeasure(Aggregate.Sum) NPV : Decimal} 
[@CubeMeasure(Aggregate.StdDev) Deviation = NPV, @CubeMeasure(Aggregate.Percentile, 95) PFE = NPV];
</code></pre>
<p>is equivalent to</p>
<table>
<thead>
<tr>
<th>┃</th>
<th>Name</th>
<th>┃</th>
<th>Notes</th>
<th>┃</th>
</tr>
</thead>
<tbody>
<tr>
<td>┃</td>
<td>NPV</td>
<td>┃</td>
<td>Total across dimensions, up through hierarchies</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>Deviation</td>
<td>┃</td>
<td>Populate standard deviation for all elements</td>
<td>┃</td>
</tr>
<tr>
<td>┃</td>
<td>PFE</td>
<td>┃</td>
<td>The top 95% of all simulated scenarios across the entire set at each  level</td>
<td>┃</td>
</tr>
</tbody>
</table>
<p>When the <em>Hiperspace</em> is hosted using <strong>Hiperspace.DB</strong> the <em>_Fact</em> and <em>_Cube</em> elements are automatically recalculated:</p>
<ul>
<li>When a <em>Fact</em> changes the <strong>_Fact</strong> entity is recalculated with its <em>dimension</em> references</li>
<li>When an element that a <em>Fact</em> is dependent on changes the <strong>_Fact</strong> entity is recalculated</li>
<li>When a <strong>Dimension</strong> changes, any <strong>_Cube</strong> elements that reference it are recalculated.</li>
</ul>
<p>Because <strong>_Facts</strong>  and <strong>_Cubes</strong> are recalculated using a <em>delta</em> of changed elements in real-time, it is possible for <strong>Facts</strong> to be calculated from other aggregated <strong>Facts</strong>. Both <em><strong>Facts</strong></em> and  <em><strong>Cubes</strong></em> track history  through time (<em>using <code>GetVersions()</code> functions</em>)</p>
<hr>
<p>Relase also includes updates to reference assemblies</p>
]]></description>
      <pubDate>Sat, 14 Feb 2026 16:09:14 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260214</guid>
    </item>
    <item>
      <title>Release 30th January 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260130</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release is focused on improvements to the Cube functionality to provide a foundation for viewing Cube's through Hiperspace.DB's Blazor.   The Blazor explorer uses</p>
<ul>
<li>Cube Dimension Name to display the next of the element in a PivotTable view</li>
<li>Cube Hierarchy Level to enable the PivotTable to view to drilldown through hierarchies of elements</li>
</ul>
<p>For  <em>Portfolio</em> Cube dimension with three levels in the hierarchy, the cube will be displayed in a Pivot -Table as {Portfolio 1, Portfolio 2, Portfolio 3}, and support drilldown from the highest level</p>
<pre><code>@CubeDimension(Id), CubeHierarchy(Parent, Tier), Versioned
entity Cube.Portfolio 
	= Node (SKey = SKey, Name = Id, TypeName = "Portfolio"),
	  Cube.Edges (From = this, To = Parent, FromTypeName = "Portfolio-Parent", ToTypeName = "Portfolio-Child", Name = Id)
(Id : String) 
{ Parent : Cube.Portfolio, Tier : Int32 } 
[Children : Cube.Portfolio (Parent = this)];
</code></pre>
<h2>Hiperspace</h2>
<p><code>CubeDimension</code> has been updated to include a reference to the name that should be used when the Cube is viewed in a Piviot-table view.
<code>CubeHierarchy</code> has been updated to include a level indicator to allow Pivot-table views to navigate through hierarchy of elements</p>
<h2>HiLang</h2>
<p>###empty view enhancement
The view <code>Cube.Edge_</code> and <code>Cube.Edges</code> could be used as helpers to provide additional ways to project <em>elements</em> as graph views, but if they are not referenced elsewhere in the model, they will be pruned and not have any code generation.</p>
<pre><code>"An additional edge helper for Cube.Edges"
view Cube.Edge_ : Edge = Edge();

"Bidirectional Edge, implemented with two Cube.Edges"
view Cube.Edges
    = Edge  (From = From, To = To, Name = Name, TypeName = FromTypeName),
      Cube.Edge_ (From = To, To = From, Name = Name, TypeName = ToTypeName)
(
    From            : Node      #7,
    To              : Node      #8,
    FromTypeName    : String    #9,
    ToTypeName      : String    #10
)
{
    Name        : String    #6,
};
</code></pre>
<p>This presents a problem for the generation of the <code>Edge</code> subspace, since it expects to add a base reference to the pruned view.  A fix has been added to the HiLang compiler to remove the dependency.  The compiler pipeline has been changed to continue process <em>transformations</em> to prevent spurious errors being created from <em>validation</em> that depends on prior <em>transformations</em> .</p>
]]></description>
      <pubDate>Fri, 30 Jan 2026 23:15:26 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260130</guid>
    </item>
    <item>
      <title>Release 17th January 2026</title>
      <link>https://www.cepheis.com/hiperspace/20260117</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release focuses on foundations to support of the migration of data from the current model of using key prefix for exceptions ("00" MetaModel, "0.." versioned, "00.." vector, "000.." sequence), to using the first-byte as a prefix for key-type.  To address this need, <strong>dump / load</strong> functions have been added to <code>SubSpace</code> for JSON data interchange"</p>
<h2>SubSpace</h2>
<p>The JSON format is structured as <code>{ Key : Element { ....}, AsAt : timestamp, Value : Element { ....}}}</code></p>
<ul>
<li>IEnumerable&lt;string&gt; Dump() : <em>dump the content of the hiperspace to JSON</em></li>
<li>Load(IEnumerable&lt;string&gt; source) : <em>load the JSON to Hiperspace</em></li>
</ul>
]]></description>
      <pubDate>Sat, 17 Jan 2026 21:37:20 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20260117</guid>
    </item>
    <item>
      <title>Release 31st December 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251231</link>
      <description><![CDATA[<h1>Overview</h1>
<p>Change to the <code>GetSequence</code> and <code>UseSequence</code> functions to take a parameter to allow the use of <code>KeyType</code> for Sequences</p>
<p>Extended  <code>Session2(DateTime? AsAt = null)</code>  to <code>Session2(DateTime? AsAt = null, DateTime? DeltaFrom = null)</code> to allow the DeltaFrom parameter to be used in addition to AsAt.</p>
<h2>Obsolete</h2>
<p>Several interfaces function have been marked as <code>Obsolete</code> and will be removed in the next version.</p>
<p>Currently <strong>Hiperspace</strong> uses a key prefix convention to distinguish <em>immutable</em> and <em>versioned</em> elements, <em>VectorSpaces</em> (for nearest neighbor search) and <em>sequences</em></p>
<table>
<thead>
<tr>
<th>│</th>
<th>Prefix</th>
<th>│</th>
<th>Description</th>
<th>│</th>
</tr>
</thead>
<tbody>
<tr>
<td>│</td>
<td>00</td>
<td>│</td>
<td>The meta data dictionary for the <em>Hiperspace</em>, used to ensure that incompatible libraries cannot corrupt a store</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>0...</td>
<td>│</td>
<td>Versioned element that includes a <em>timestamp</em> suffix at the end of the key</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>00...</td>
<td>│</td>
<td>VectorSpace indexes</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>001...</td>
<td>│</td>
<td>Sequence numbers for mutable items</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td></td>
<td>│</td>
<td>Keys without a prefix are immutable elements without version</td>
<td>│</td>
</tr>
</tbody>
</table>
<p>Performance testing of <strong>Hiperspace.DB</strong>  has highlighted that the rebuilding of the <code>MetaMap</code> used to translate between protobuf <em>tag/length/value</em> to <em>tag/value/length</em> for index search can be expensive top build when a store has experienced multiple schema changes.  The <strong>next</strong> version will change the prefix to:</p>
<table>
<thead>
<tr>
<th>│</th>
<th>Prefix</th>
<th>│</th>
<th>Description</th>
<th>│</th>
</tr>
</thead>
<tbody>
<tr>
<td>│</td>
<td>1</td>
<td>│</td>
<td>The meta data dictionary for the <em>Hiperspace</em>, used to ensure that incompatible libraries cannot corrupt a store</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>2</td>
<td>│</td>
<td>cached <code>MetaMap</code> entries for key encode/decode</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>3</td>
<td>│</td>
<td>validated client library <em>fingerprint</em> to avoid the need for <code>MetaMap</code> build</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>4</td>
<td>│</td>
<td>immutable elements without version suffix</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>5</td>
<td>│</td>
<td>Versioned element that includes a <em>timestamp</em> suffix at the end of the key</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>6</td>
<td>│</td>
<td>VectorSpace indexes</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>7</td>
<td>│</td>
<td>Sequence numbers for mutable items</td>
<td>│</td>
</tr>
</tbody>
</table>
<p>The <code>#id</code> for {<code>Node</code>, <code>Edge</code>, <code>VectorNode</code>, <code>HiperEdge</code>,<code>PathMessage</code>} will also be moved to a <em>reserved</em> set of low values with all <em>domain</em> keys moved to higher values</p>
<hr>
<h2>SetSpace</h2>
<p>Added two functions to get a sequence counter from the driver:</p>
<ul>
<li><code>GetSequenceAsync&lt;T&gt;(T element)</code> to get the current sequence number associated with the domain key</li>
<li><code>UseSequenceAsync&lt;T&gt;(T element)</code> to get a new  sequence number for the with the domain key</li>
</ul>
<p><strong>NB</strong> While the main use of Sequence numbers is to provide an alternate (<em>GPGPU friendly</em>) sequence number for a <code>Node</code>, it can be applied to any key value.</p>
<pre><code>// get an order number that is unique within all orders
var orderNo = await space.Orders.UseSequenceAsync (new Order { });
var orderNo = await space.Orders.UseSequenceAsync (new Order.KeyType { });

// get a line number within the owning Order {1,2,3..}
var lineNo = await space.Lines.UseSequenceAsync (new Line { Order = order }); 
var lineNo = await space.Lines.UseSequenceAsync (new Line.KeyType { Order = order.self }); 
</code></pre>
<p><code>Get</code> and <code>GetASync()</code> functions now handle not found errors by returning <code>null</code> for scenarios where a reference object cannot be found.  This is especially useful for views where not found is better handled by <code>Horizon&lt;&gt;</code> filters that</p>
<hr>
<h2>Bind Version</h2>
<p>All versions of Bind Function have been marked as Obsolete, with a preference to use the optimistic locking instead (that include the original AsAt copied to AsWas for comparison).
Versions of Bind functions that include the <code>priorVersion</code> DateTime value use optimistic concurrency control to prevent updates to a version that has been changed by another session</p>
<h2>Nearest</h2>
<p>Hiperspace provides nearest neighbor search using a VectorSpace <code>aspect</code> for geospatial and RAG search.  These functions have been extended to include a maximum distance search</p>
<hr>
<h2>Hilang</h2>
<p>The Hilang ahead of time compiler has been updated to remove indexes that are a subset of the element key, and will not be used</p>
<hr>
<h2>Hiperspace.Rocks</h2>
<p>The <strong>RocksDB</strong> driver has been upgraded to verion 10.4.2.63147</p>
]]></description>
      <pubDate>Wed, 31 Dec 2025 17:15:47 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251231</guid>
    </item>
    <item>
      <title>Release 21st December 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251221</link>
      <description><![CDATA[<h1>Overview</h1>
<p>Change method signatures for Path functions to use Route instead of RouteMap for remote calls.</p>
<p>Add Session2 to Subspace to support time-travel sessions over a graph space to support partial refresh of GPU cache for GPU parallel search of Graph methods without the need for recursive cycle detection.</p>
<hr>
<h2>Sequence support</h2>
<p>Sequence numbers are a relational database feature that has largely been replaced by <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">UUID</a> (<em>especially v7</em>) since they do not require a sequence source and can be allocated within a client.  They still have a use as an alternate key especially with <a href="https://en.wikipedia.org/wiki/General-purpose_computing_on_graphics_processing_units">GPGPU</a> that need primitive points, <em>but do not wish to use the expense of mapping UUID to int128 values that may not be supported by the device</em></p>
<p><strong>Hiperspace</strong> implements <em>sequences</em> as unsigned 64-bit integers (<em>1 to 18,446,744,073,709,551,616</em>), and can be stored for any <em>element</em> key value</p>
<h3>Hiperspace</h3>
<p>Added <code>UseSequenceAsync()</code> for driver implementation, together with proxy methods in {<code>BrowserSpace, FailoverSpace, GenerationSpace, PartitionSpace, SessionSpace</code>}</p>
<h3>Hiperspace.Heap</h3>
<p>Implemented  <code>UseSequenceAsync()</code> in <code>Hiperspace.Heap</code> and added additional driver <code>CacheHeapSpace</code> for use by caches that do not require history to be maintained for elements. <strong>CacheHeapSpace</strong> should <em>only</em> be used by <em>caches</em> since it lacks the ability to reconcile concurrent changes through version inspection.</p>
<p><code>LatestSpace</code> is replaced by <code>CacheHeapSpace</code> and will be removed in a future release.</p>
<h3>Hiperspace.Rocks</h3>
<p>Implemented UseSequenceAsync()<code>in</code>Hiperspace.Rocks<code>and added additional driver</code>CacheRockSpace` for use by caches that do not require history to be maintained for elements. <strong>CacheRockSpace</strong> should <em>only</em> be used by <em>caches</em> since it lacks the ability to reconcile concurrent changes through version inspection.</p>
<p>The underlying RocksDB driver has been updated.</p>
<h3>SubSpace</h3>
<p><code>SubSpace</code> forwards the <code>UseSequenceAsync()</code> function to the underlying driver</p>
<h3>SetSpace</h3>
<p>Added two functions to get a sequence counter from the driver:</p>
<ul>
<li><code>GetSequenceAsync&lt;TEntity&gt;(TEntity? element)</code> to get the current sequence number associated with the domain key</li>
<li><code>UseSequenceAsync&lt;TEntity&gt;(TEntity? element)</code> to get a new  sequence number for the with the domain key</li>
</ul>
<p><strong>NB</strong> While the main use of Sequence numbers is to provide an alternate (<em>GPGPU friendly</em>) sequence number for a <code>Node</code>, it can be applied to any key value.</p>
<pre><code>// get an order number that is unique within all orders
var orderNo = await space.Orders.UseSequenceAsync (new Order { });

// get a line number within the owning Order {1,2,3..}
var lineNo = await space.Lines.UseSequenceAsync (new Line { Order = order }); 
</code></pre>
<hr>
<h3>HiLang</h3>
<p>Added code generation for the SubSpace methods.</p>
<hr>
<h2>OnInitialized</h2>
<p>Addition of <code>virtual void OnInitialized()</code> function to SubSpace to allow domian subspaces to perform additional initialization after the Domain Space has been created by a generic container (e.g. <strong>Hiperspace.DB</strong>)</p>
]]></description>
      <pubDate>Mon, 22 Dec 2025 12:40:54 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251221</guid>
    </item>
    <item>
      <title>Release 10th December 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251210</link>
      <description><![CDATA[<h1>Overview</h1>
<p>This release introduces GPU optimization for graph searches involving <code>HiperEdge</code> and other vector operations used in nearest-neighbor searches (<em>commonly required when providing information for AI prompts</em>).</p>
<p>It also enhances <em>Hiperspace</em> functions for LINQ queries by adding automatic null-coalescing for values selected from Hiperspace, supporting non-sargable join conditions, and improving performance for multi-table joins and <strong>HiperspaceDB</strong> cube aggregation.</p>
<hr>
<h2>CalculationGPU</h2>
<p><a href="https://en.wikipedia.org/wiki/Graph_theory">Graph Theory</a> is a branch of Mathematics that can be used to describe all knowledge as <code>Node</code> and <code>Edge</code>'s  between them; because there is a simple consistent model across domains it has wide application for visualizing information, but has the downside that many graph views appear as a tangled web of nodes and lines: <em>often</em> conveying complexity, <em>but not</em> clarity.</p>
<p><a href="https://en.wikipedia.org/wiki/Hypergraph">HyperGraph</a>  is a generalization of <a href="https://en.wikipedia.org/wiki/Graph_theory">Graph</a> that replaces the tangle of edges with a <em>hyperedge</em> that encapsulates all the intermediate nodes that are not interesting: I have a <em>cousin</em> (Robert), which can be described as "<code>Me-&gt;(cousin)-&gt;Robert</code>", but <em>cousin</em> is actually a <em>hyperedge</em> that is derived from "<code>me-&gt;mother2-&gt;mother1-&gt;daughter-&gt;son where mother2 is not daughter</code>"</p>
<p><a href="https://www.cepheis.com/hiperspace/hiperedge">HiperEdge</a> is a <em>HiperSpace</em> implementation of <em>hyperedge</em> that encapsulates the source of the <em>hyperedge</em> for a simple view of connections <em><strong>or</strong></em> the intermediate nodes in the path. When viewed graphically a <code>HiperEdge</code> view looks like a tree of connections extending out from the subject <code>Node</code>.</p>
<p>Hiperspace provides functions to query  <a href="https://www.cepheis.com/hiperspace/hiperedge">HiperEdge</a>s using rules that project {<em>source node type, edge type, end node type</em>} to create <a href="https://www.cepheis.com/hiperspace/hiperedge">HiperEdge</a>s whenever needed (<em>e.g. as a member function of a <code>Node</code></em>).</p>
<h3><em>Graph support</em></h3>
<p>This version introduces <code>ICalculationGPU</code> for optional acceleration of Graph Queries using the hardware created for <a href="https://en.wikipedia.org/wiki/Ray_tracing_(graphics)">Ray Tracing</a>,</p>
<h3><em>Calculation support</em></h3>
<p>Search for <em>Nearest</em> node proximity in a <a href="https://en.wikipedia.org/wiki/Vector_space">Vector Space</a> is supported via GPU, together with aggregation of Vectors used for complex calculation.</p>
<hr>
<h3>Null Coalesce</h3>
<p>Consider a cube analytics model with {<em>Sector, Customer, Account, Trade</em>} with dimensions <em>Customer</em> and <em>Sector</em>; with <em>Trade</em> Facts aggregated. Null-coalescing is necessary when querying dimensions associated with a <em>Trade</em> fact.</p>
<pre><code>@CubeDimension, CubeHierarchy(Parent), Versioned
entity Sector 
  (Id : Int32) 
  {Name : String, Parent : Sector} 
  [Children : Sector (Parent = this), Customers : Customer (Sector = this)];

@CubeDimension, DeltaIndex
entity Customer 
  (Id : String) 
  {Sector : Sector}
  [Name : String, Accounts : Account (Customer = this)];
entity Instrument;

entity Account 
  (Id : String) 
  {Name : String, Customer : Customer};

@CubeFact, DeltaIndex
entity Trade : Versioned 
  (id : Int64) 
  {@CubeMeasure(Aggregation.Sum) Quantity : Decimal, Instrument : Instrument, Account : Account};
</code></pre>
<p><img src="/hiperspace/media/Sites/hiperspace/sample/trade-null.png"></p>
<p>The query  <code>from trade in Space.Trades select trade.Account.Customer.Sector</code> provides the <em>Sector</em> for a trade, but if any {<em>Trade.Account, Account.Customer, Customer.Sector</em>} is missing the expression will break.  The work around is to manually add null-coalesce to the query:</p>
<pre><code>from trade in Space.Trades 
select trade.Account ==  null ? null :  
       trade.Account.Customer == null ? null :
       trade.Account.Customer.Sector;
</code></pre>
<p><strong>This release adds coalesce automatically</strong>. This is especially useful when Hiperspace.SQL is used to query a <em>hiperspace</em> from a scripting language where null-coalesce is not available, but we want to access the <em>graph</em> of properties.</p>
<pre><code>select c.Id as SectorId, a.Id as AccountId
from Account as a join 
     Customer as c on a.Customer.Id = c.Id
group by c.Sector.Id, a.Id;
</code></pre>
<p>This query would have <em><strong>previously</strong></em> failed if <code>a.Customer</code> is missing.</p>
<hr>
<h3>Non-Saragable predicates</h3>
<p>In <em>Hiperspace</em> the following query cannot be <a href="https://en.wikipedia.org/wiki/Sargable">Sargable</a> since <em>Customer</em> does not contain a <em>Sector</em> but a <code>KeyRef&lt;Sector.KeyType, Sector&gt;</code> with <em>lazy</em> loading of the element if needed.</p>
<pre><code>select Sector.Name as "Sector Name", c.Name as "Customer Name"
from   Sector join
       Customer on Sector.Name = Customer.Sector.Name
order by Sector.Name, Customer.Name;
</code></pre>
<p>This query would <em><strong>previously</strong></em> have failed since <code>Customer.Sector</code> is a derived property of <em>Customer</em> and cannot be applied to a template for a <code>Find()</code> request of matching <em>Customers</em></p>
<p>This release splits join processing into <a href="https://en.wikipedia.org/wiki/Sargable">Sargable</a> predicates and residual conditions and is constructed dynamically as compiled expressions.</p>
]]></description>
      <pubDate>Wed, 10 Dec 2025 21:31:56 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251210</guid>
    </item>
    <item>
      <title>Release 3rd December 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251303</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This release adds <code>NotFoundException</code> to distinguish <em>not found</em> from <em>cannot be found</em> conditions. and extends the functionality of <code>@AlternateIndex</code> to support multiple alternate indexes on <em>segments</em> and <em>aspects</em> that are referenced by multiple <em>entities</em>.</p>
<hr>
<h3>Not Found</h3>
<p>Prior to this release <code>Get(...)</code> calls did not distinguish between <em>Not Found</em> and <em>not found because of IO error</em>.  To improve the handling of missing values several changes have been made:</p>
<ul>
<li>Additional Exception class <code>NotFoundException</code></li>
<li><code>KeyRef&lt;&gt;</code> (<em>reference to another element</em>) changed to return <code>null</code> when a value cannot be found</li>
<li><code>RefSingle&lt;&gt;</code> (<em>reference to an aspect</em>) changed to return <code>null</code> when a value cannot be found</li>
</ul>
<hr>
<h3>AlternateIndex</h3>
<p>Alternate indexes are created automatically whenever there is a path from an <em>element</em> from another <em>element</em>, but can be added to support access from a view.  The prime example is <code>Edge</code> which is defined (<em>in the Hilang prelude</em>) as</p>
<pre><code>"edge between nodes"
view Edge 
( /* keys */
    From        : Node,
    To          : Node,
    TypeName    : String
)
{ /* values */
    Name        : String
};
</code></pre>
<p><code>@AlternateIndex</code> enables an <em>element</em> to index the <em>key/value</em> that corresponds to the <code>From</code> key member for indexed access from a <code>Node.Froms</code> extension property</p>
<p>For the model</p>
<pre><code>entity CostCentre (Id : Int32) [Costs : Cost (CostCentre = this)];
aspect Cost {CostCentre : CostCentre, Amount : Decimal};
</code></pre>
<p>with</p>
<pre><code>entity Asset (...) {...} [Cost : Cost];
entity Project (...) {...} [Cost : Cost];
</code></pre>
<p>Concreate elements <code>AssetCost</code> and <code>ProjectCost</code> will create indexes
<code>AssetCostCostCentre.Index</code> and <code>ProjectCostCostCentre.Index</code></p>
<p>Source edit will  change the source to</p>
<pre><code>entity CostCentre (Id : Int32) [Costs : Cost (CostCentre = this)];
aspect Cost 
{
  @AlternateIndex("AssetCost", 42)
  ,AlternateIndex("ProjectCost", 43) 
  CostCentre : CostCentre, Amount : Decimal};
</code></pre>
<p>To ensure the index Id is not used for something else resulting in an incompatible model and store.</p>
<p><em><strong>how does CostCentre know what (Asset/Project/ etc) the Cost is for?</strong></em>
The <code>aspect Cost</code> is transformed to a <code>view</code> that is equvilent to</p>
<pre><code>view Cost (owner : Any) {CostCentre : CostCentre, Amount : Decimal};
</code></pre>
<p>the (<em>C#</em>) hiperspace query</p>
<pre><code>from centre in space.CostCentres 
select centre.Id, (from line in centre.Costs 
                   let asset = line.owner.Is&lt;Asset&gt;() ? Amount : 0 
                   let project = line.owner.Is&lt;Project&gt;() ? Amount : 0 
                   group line by line.CostCentre into totals
                   select new { Projects = totals.Sum(v =&gt; v.asset),
                                Assets = totals.Sum(v =&gt; v.project)})
</code></pre>
<p>Will return the total costs by type for each CostCentre</p>
<h4>Inherited Index</h4>
<p>This model defines an overall <em>trade</em> type with three different implementations for {<em>FI, EQ, FX</em>}  that have different properties for the different asset-classes.   <em>Trade</em> is referenced by <em>Book</em>,  the extension property <em><code>Book.Trades</code></em> returns a collection of <em>Trade</em> that has a <em><code>Book</code></em> equal to the current <em>Book</em>.  For efficient access, an index is created for the <em><code>Trade.Book</code></em> that is inherited by each implementation.</p>
<p>The syntax <code>Banking.FI.Trade : Banking.Trade = Banking.Trade()</code> means <code>Banking.FI.Trade</code>:</p>
<ul>
<li>Inherits <em>keys / values / extensions / properties</em> from <code>Banking.Trade</code> (via <strong><code>:</code></strong>)</li>
<li>Can be viewed as a <code>Banking.Trade</code> (via <strong><code>=</code></strong>)</li>
</ul>
<pre><code>view Banking.Trade (Id : String) 
{Book : Banking.Book};
entity Banking.FI.Trade : Banking.Trade = Banking.Trade();
entity Banking.FX.Trade : Banking.Trade = Banking.Trade();
entity Banking.EQ.Trade : Banking.Trade = Banking.Trade();
entity Banking.Book (Id : String) [Trades : Banking.Trade (Book = this)];
</code></pre>
<p>Adding <code>%ids</code> to the model, with result in a source edit to a <code>#</code> id to each <em>element</em>, <em>key/value</em> and extension property, and <code>@AlternateIndex</code> property for each generated concrete index.</p>
<pre><code>view Banking.Trade #45 (Id : String) 
{@AlternateIndex("Banking.EQ.Trade", 52) 
 ,AlternateIndex("Banking.FI.Trade", 48)
 ,AlternateIndex("Banking.FX.Trade", 50) 
 Book : Banking.Book};
entity Banking.FI.Trade : Banking.Trade = Banking.Trade() #49;
entity Banking.FX.Trade : Banking.Trade = Banking.Trade() #51;
entity Banking.EQ.Trade : Banking.Trade = Banking.Trade() #53;
entity Banking.Book #47 (Id : String #1) [Trades : Banking.Trade (Book = this) #54];
</code></pre>
<p>subsequent compilation of the model will result in the indexes using the same <em>Id</em> value when stored.<br>
<strong>NB</strong> the <code>#id</code> can be of any value, but can never to reused for a different purpose once used with a <em>Hiperspace</em>.</p>
<hr>
<h3>Source Editing</h3>
<p>When the directive <code>%ids</code> is added to a hilang model, the source code is edited to add <code>#id</code> values to ensure that the schema can be evolved without the risk of introducing incompatible changes.</p>
<p><em><strong>know issue</strong></em>: Source editing lacks the context of other edits to a line of <code>hilang</code> source <code>@AlternateIndex("Banking.EQ.Trade", 52) @AlternateIndex("Banking.FI.Trade", 48) Book : Banking.Book</code> will create a syntax error the next time the <em>schema</em> is compiled because <code>@</code> is the prefix for a comma-separated list of one-or-more attributes.  The code needs to be edited to change subsequent <code>@</code> to <code>,</code></p>
]]></description>
      <pubDate>Sun, 07 Dec 2025 15:45:23 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251303</guid>
    </item>
    <item>
      <title>Release 24th November 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251124</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This release adds the <code>@AlternateIndex</code> property to explicitly create an index for views and ad hoc queries that do not <em>or cannot</em> be defined in the normal way of using an access path.</p>
<h3>Path Index</h3>
<p>Normally indexes are created in <code>Hiperspace</code> by defining an extent that uses an attribute to retrieve a list of elements.  The following hilang model defines a <em>Person</em> with a <em>Parent</em> and an extension <em>Children</em> that retrieves all <em>Person</em> that have a <em>Parent</em> that refers to this <em>person</em>.</p>
<pre><code>entity Person (Id : Guid) {Parent : Person} [Children : Person (Parent = this)];
</code></pre>
<p>The <em>Children</em> extent causes an index to be created on <em>Parent</em> so that access to <em>Children</em> is fast and does not require an scan of all <em>Person</em> elements</p>
<h3>Alternate Index</h3>
<p>The following model defines two entities {<em>Product, Order</em>} that can be viewed as <strong>nodes</strong> in a <strong>graph</strong>. The segment <em>Item</em> defined the details of the <em>Order</em> with reference to the <em>Product</em> being ordered.</p>
<pre><code>entity Product = Node(...) (Id : Guid);
entity Order = Node (...) (Id : Guid) [Items : Item];
segment Item = Edges (From = owner, To = Product, FromType = "Purchase", ToType ="Purchased")
(Line : Int32) 
{
  @AlternateIndex
  Product : Product,
  Quantity : Decimal
};
</code></pre>
<p>In a <strong>UML</strong> diagram,  <em>Order</em> aggregates <em>Item</em>, and <em>Item</em> is associated with <em>Product</em>.  In a <strong>Graph</strong> diagram <em>Order</em> and <em>Product</em> are both <code>Nodes</code> with two <code>Edge</code> between them for "Purchase" and "Purchased".</p>
<p><img src="/hiperspace/media/Sites/hiperspace/sample/orderitem.png"></p>
<p><em>Product</em> cannot refer to <em>Item</em> because it is a <code>segment</code> of its owner (<em>in this case Order</em>), and cannot therefore define a <em>path</em> from <em>product</em> to <em>Item</em>. When viewed as a <strong>graph</strong> the Order-&gt;(Purchase)-&gt;Product <code>Edge</code> uses the <em>Item</em> key to find all edges, but the Product-&gt;(Purchased)-&gt;Order <code>Edge</code> needs an <code>@AlternateIndex</code> to avoid a scan of all <em>Items</em>.</p>
<hr>
<h2>Result&lt;&gt; enhancement</h2>
<p>The Hiperspace <a href="https://github.com/channell/Hiperspace/blob/master/src/Hiperspace/Result.cs">Result</a> type has been updated to include <em>Rust-Style</em> continuation monads that can optionally follow a <code>.Bind()</code> (<em>bind an element with hiperspace</em>) with:</p>
<ul>
<li><code>.Then</code> chaining function</li>
<li><code>.Else</code> error handling function</li>
</ul>
<pre><code>write.Roles
    .Bind(new Role
    {
        Name = role,
        RoleName = role,
    })
    .Then(r =&gt;
    {
        Log?.LogInformation($"Added missing role {r.Name}");
        return Result.Ok(r);
    })
    .Else(r =&gt; Log?.LogError($"Failed to bind role {r.Value.Name} with status {r.Status} and reason {r.Reason}"));
</code></pre>
]]></description>
      <pubDate>Mon, 24 Nov 2025 20:12:29 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251124</guid>
    </item>
    <item>
      <title>Release 15th November 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251115</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This version updates to .NET 10 and modernizes the code to use <code>name != null</code> and <code>ReferenceEquals(name, null)</code>
to <code>name is not null</code>.</p>
<p>The <code>is null</code> and <code>is not null</code> patterns are <strong>billed</strong> as</p>
<blockquote>
<p>provide a clearer and more concise way to check for null values in C#. This change enhances readability and aligns with current C# best practices.</p>
</blockquote>
<p>but the main advantage is that:</p>
<ul>
<li>You don't need to include <code>ReferenceEquals</code> in any <code>operator ==</code> overload.</li>
<li><code>name is not null</code> is the same for reference (<em>class</em>) and value (<em>struct</em>) objects instead of <code>ReferenceEquals(name, null)</code> for classes and <code>!name.HasValue</code> for nullable values.</li>
</ul>
<h2>References</h2>
<p>Reference libraries have also been updated</p>
]]></description>
      <pubDate>Sat, 15 Nov 2025 17:12:48 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251115</guid>
    </item>
    <item>
      <title>Release 6th November 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251106</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This release builds on the <a href="https://www.cepheis.com/hiperspace/hiperedge">HiperEdge</a> functionality provided by <strong>message</strong> to execute queries on a remote <strong>Hiperspace.DB</strong> and return the full set of <em>elements</em> back to a client without the need to return every intermediate object necessary.  The prime example is the <strong>Hiperspace.DB</strong> opening page, which displays a summary bar-graphs of the number of <strong>nodes</strong> and <strong>edges</strong> in each database, and validates that <em>changes</em> to the (compiled) schema for databases does not change the definition of an already stored <em>element set.</em></p>
<p>The <em>graph-view</em> functionality of <strong>Hiperspace.DB</strong> uses <code>HiperEdge</code> functions to recursively search (<em>in parallel</em>) all <code>Node</code> types that satisfy the view criteria and display as a navigable <a href="https://en.wikipedia.org/wiki/SVG">SVG</a> graph of connections.  These capabilities use the <a href="https://github.com/channell/Hiperspace/blob/master/src/Hiperspace/Graph/GraphFunctions.cs"><code>GraphFunctions</code></a> to search Nodes using message-keys <em>sent</em> to the server and message-value <em>provided</em> on completion together with rendering information for <code>Node</code> <em>shapes</em> and <em>color</em>.</p>
<p>To support these use-cases, a couple of enhancements have been added to <a href="https://www.nuget.org/packages/Hiperspace/">Hiperspace</a> and <a href="https://www.nuget.org/packages/HiLang">HiLang</a> to better support the functions.</p>
<h3>SubSpace</h3>
<p><code>IServiceProvider</code> is  an interface on the .NET Platform to inject <em>Services</em> into components (<a href="https://en.wikipedia.org/wiki/Inversion_of_control">IoC</a>) at runtime.  A ServiceProvider property has been added to <code>SubSpace</code> to enable messages to enlist functionality when a message is received, and a parameter added to <code>SubSpaceParameters</code> for <em>domain space</em> construction.</p>
<p><code>FindPaths</code> and <code>FindPathsAsync</code> functions now have <em>domain-specific</em> implementations that use a <em><strong>message</strong></em> to transmit the call through to a server where the parallel recursive search can be efficiently executed close the data, using many of the cores of the server.</p>
<h3>Node</h3>
<p>The <code>Node</code> element has been extended to include additional functions that provide <code>HiperEdge</code>  search for related nodes across transitive edges</p>
<table>
<thead>
<tr>
<th>│</th>
<th>Name</th>
<th>│</th>
<th>Description</th>
<th>│</th>
</tr>
</thead>
<tbody>
<tr>
<td>+</td>
<td>━━━━━━━━</td>
<td>+</td>
<td>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</td>
<td>+</td>
</tr>
<tr>
<td>│</td>
<td>HiperEdges</td>
<td>│</td>
<td>Treat the TypeName parameter as a HiperEdge and find all matching</td>
<td>│</td>
</tr>
<tr>
<td>│</td>
<td>HiperEdgesAsync</td>
<td>│</td>
<td>as above, but non-blocking (especially for Blazor web clients)</td>
<td>│</td>
</tr>
</tbody>
</table>
<pre><code>        /// &lt;summary&gt;
        /// Treat the Edges of TypeName as a HiperEdge and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="TypeName"&gt;The Edge type name&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public HashSet&lt;HiperEdge&gt; HiperEdges(string TypeName, int? length = null, HashSet&lt;string&gt;? targets = null)

        /// &lt;summary&gt;
        /// Create an inline HiperName from TypeNames and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="HiperName"&gt;The name of the HiperEdge being infered from the Edge TyopeNames&lt;/param&gt;
        /// &lt;param name="TypeNames"&gt;the set of Edge TypeNames that make up this HiperEdge&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public HashSet&lt;HiperEdge&gt; HiperEdges(string HiperName, IEnumerable&lt;string&gt; TypeNames, int? length = null, HashSet&lt;string&gt;? targets = null)

        /// &lt;summary&gt;
        /// Treat the Edges of TypeName as a HiperEdge and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="TypeName"&gt;the name given to this transitative hiperedge&lt;/param&gt;
        /// &lt;param name="rules"&gt;the set of meta edges (start-node type, end-node type, edge type) rules that define the transitative path&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public HashSet&lt;HiperEdge&gt; HiperEdges(string TypeName, HashSet&lt;Rule&gt; rules, int? length = null, HashSet&lt;string&gt;? targets = null)

        /// &lt;summary&gt;
        /// Treat the Edges of TypeName as a HiperEdge and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="TypeName"&gt;The Edge type name&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public async Task&lt;HashSet&lt;Graph.HiperEdge&gt;&gt; HiperEdgesAsync(string TypeName, int? length = null, HashSet&lt;string&gt;? targets = null, CancellationToken cancellationToken = default)

        /// &lt;summary&gt;
        /// Create an inline HiperName from TypeNames and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="HiperName"&gt;The name of the HiperEdge being infered from the Edge TyopeNames&lt;/param&gt;
        /// &lt;param name="TypeNames"&gt;the set of Edge TypeNames that make up this HiperEdge&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public async Task&lt;HashSet&lt;HiperEdge&gt;&gt; HiperEdgesAsync(string HiperName, IEnumerable&lt;string&gt; TypeNames, int? length = null, HashSet&lt;string&gt;? targets = null, CancellationToken cancellationToken = default)

        /// &lt;summary&gt;
        /// Treat the Edges of TypeName as a HiperEdge and find all transitive paths for that type
        /// &lt;/summary&gt;
        /// &lt;param name="TypeName"&gt;the name given to this transitative hiperedge&lt;/param&gt;
        /// &lt;param name="rules"&gt;the set of meta edges (start-node type, end-node type, edge type) rules that define the transitative path&lt;/param&gt;
        /// &lt;param name="length"&gt;the maximum length of the path&lt;/param&gt;
        /// &lt;param name="targets"&gt;only return HiperEdges that end with a Node of the type matching one of these types&lt;/param&gt;
        /// &lt;returns&gt;The full set of HiperEdges for this path&lt;/returns&gt;
        public async Task&lt;HashSet&lt;Graph.HiperEdge&gt;&gt; HiperEdgesAsync(string TypeName, HashSet&lt;Rule&gt; rules, int? length = null, HashSet&lt;string&gt;? targets = null, CancellationToken cancellationToken = default)
</code></pre>
<p>The <a href="https://github.com/channell/Hiperspace/blob/master/examples/CousinProblem/Test.cs">Test cases</a> demonstrate the use of these functions to provide <em>server-side</em> search for relations. The first example treats "Child" as a transitive <code>HiperEdge</code>, while the second infers an "Ancestors" <code>HiperEdge</code> from either <em>Mother</em> or <em>Father</em>.</p>
<pre><code>                var descendants = node.HiperEdges("Child");
                var ancestors = node.HiperEdges("Ancestors", new[] {"Mother", "Father" });
</code></pre>
]]></description>
      <pubDate>Sat, 08 Nov 2025 13:03:10 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251106</guid>
    </item>
    <item>
      <title>Release 23rd October 2025</title>
      <link>https://www.cepheis.com/hiperspace/20251023</link>
      <description><![CDATA[<h2>Overview</h2>
<p>This release extends the <strong>message</strong> functionality to behave more like a stored procedure call,
binding all <em>entity/segment/aspect/view</em> results to the local subspace when it completes.</p>
<p>Initially the <code>RemoteLabel</code>
flag was used to indicate which tier should invoke the message, and return results.  Test with <strong>Hiperspace.DB</strong>
highlighted that the flag is not needed - the message is now executed by the lowest <em>Hiperspace</em> that can provide a value.</p>
]]></description>
      <pubDate>Thu, 23 Oct 2025 19:03:16 GMT</pubDate>
      <guid isPermaLink="true">https://www.cepheis.com/hiperspace/20251023</guid>
    </item>
  </channel>
</rss>