重婚クラスタ循環の世代分割問題をクリア

重婚クラスタ循環を世代分割するという課題を原理的にクリアして,そのブラッシュアップを進めているが,BuildSameGeneMarriageGraphで無限ループするようになってしまった.どこかでミスっているのだが,どこで失敗しているのか特定できない.やむを得ず昨日の修正を一旦破棄して,2021-01-28の始業時バックアップに戻ることにする.昨日はまったくログを取っていないので,修正手順を再現しながらステップを追って点検してみよう.⇒バックアップはとりあえず動いている.ここからもう一度出直すことにしよう.先に進む前にまず,足元を固めるために,仕掛りになっている部分をフィックスしてしまった方がよいと思う.以下のような懸案事項がある.

  1. SIMPLEGRAPHのattributeを廃止@20201105 抽象グラフ検証系でオブジェクトではない一般データを扱えるようにするための修正だが,方針を変えて,attributeとncidを併用する方式に切り替える必要がある
  2. グラフのattributeはこれまで有意に使用されていなかったが,「多重枝」を扱う必要があり,有向グラフと無向グラフを峻別する必要も出てきたのでより重要性が増している 自己ループという属性もある
  3. SIMPLEEDGEの持っている(一部)属性をアプリに開放し,アプリが自由に使える領域に指定する 逆にグラフ検証系ではこの「ユーザパラメータ」は操作しない
  4. SIMPLEGRAPHクラスを基本クラスと応用クラスに再編する 前にUNDOシステムで行った改編と同様趣旨の修正を行い,SIMPLEGRAPHの抽象度を高める
  5. adjustGenerationRangeを停止する@20210117 動作的にはこの関数をNOPにしても問題は生じていない これが正しいとすれば,系列枠クラスのいくつかのパラメータを廃止してadjustGenerationRangeを完全に破棄すべきだろう
  6. 不可避の多重カード数と重婚クラスタ循環を分解するために除去される最小枝数が完全に一致するという予測が出てきた
  7. 重婚クラスタ循環が存在する場合と存在しない場合の論理を統合する
  8. 右手配偶者が左手本人で停止しない@20210108

結構クリティカルな修正だが,まずこれらを片付けてから前に進むのがよい.その前に上記を除く仕掛りを現状でフィックスしておこう.

  1. 重婚クラスタ循環を解決する@20210127 1箇所
  2. sameGeneCyclesを無視する@20210127 3箇所
  3. 仮修正 4箇所,暫定

まず項目1から始めよう.この修正は簡単なので,オプションを廃止し,コードを直接修正することにする.⇒しかし,これには昨日の「initializeではSIMPLEGRAPH:attributeを更新しない@20210128」が関係するので,まず,この修正を入れておいた方が安全だ.attributeはオブジェクト生成時に指定するか,ないしsetAttributeで明示的に設定するかのいずれかに限定するというのがこの修正だ.この修正は8箇所ある.⇒修正した.この修正はこのままフィックスする.⇒これでグラフ検証系で非モノデータを扱える準備ができた.

項目2 ⇒これは昨日の修正「無向グラフでは並行枝を登録しない@20210128」に関わりがあるので,後回し.項目3,ユーザに解放する領域(ユーザ変数)とは以下を言う.

  1. nodule *reffer;    // 参照ノード1
  2. nodule *reff2;    // 参照ノード2
  3. int usable;        // 有効な枝
  4. int nouse;            // 切断された枝
  5. int circular;        // 循環する枝(閉路上の枝)
  6. int selfloop;        // 自己ループ枝

これらはユーザが独自に定義して自由使用できる変数とし,アクセス関数は設けない.ただし,selfloopはグラフ処理系でも判断できるので,グラフ関数で設定した方がよいかもしれない.SELFLOOPGRAPHというグラフ属性を追加したので,selfloopはSELFLOOPGRAPH属性を持つグラフに対してのみ許すとした方がよいと思う.circularを閉路上の枝と定義すれば,グラフ処理系で操作する対象となるが,この変数は必ずしも閉路上の枝にかならず設定されるというものではないので,やはりアプリ側で管理するとした方がよい.現行ではおおよそそのような棲み分けになっていると思われるが,項目4を実施すればはっきりするだろう.

その前に昨日の修正「無向グラフでは並行枝を登録しない@20210128」を片付けておこう.⇒修正はSIMPLEGRAPH::addとfindedgeの2箇所だけだ.⇒問題なく動作している.現行では有向グラフを使っているのはTribeTreeGraph(系列木枝グラフ)だけのようだ.しかし,多重グラフ2も有向グラフのはずなので,設定してみよう.まずい.どこかで問題のある修正を入れてしまっているようだ.重婚同類=0 多重=10になってしまっている.上記の「無向グラフでは並行枝を登録しない@20210128」という修正だ.⇒初歩的な論理ミスがあった.

多重グラフ1(婚姻関係グラフ)で「ループフリーグラフで自己ループ枝の登録は不可」というエラーが出ている.婚姻グラフでは単身婚を自己ループとして登録している.この枝を弾いても動作にはほとんど影響は出ていないものと見られるが,単身婚以外の結婚を持たないノードの子ども世代とこのノードの親ノードの世代で重婚クラスタ循環が起きる可能性はゼロとは言えないので,このグラフが自己ループを持つのを認めることにしておこう.

系列木グラフでも同様のエラーが出ている.系列木グラフでは始系列を自己ループで登録している.これはこれでよいのではないだろうか?系列木グラフにもSELFLOOPGRAPH属性を与えておくことにしよう.グラフが大分精密に操作できるようになってきた.項目6の「不可避の多重カード数と重婚クラスタ循環を分解するために除去される最小枝数の一致」という予測を確認するためには昨日の修正を入れてみる必要がある.「多重婚配偶者は切り離す@20210128」という修正だ.⇒どうもこの修正はまだ収束していないようだ.

TestInevitableMultiZeroに入って,TestStronglyConnectedで停止してしまう.BuildSameGeneMarriageGraphのループでTestStronglyConnectedでなくなるまでループしても同じ結果になる.これは,BuildSameGeneMarriageGraphの出口で循環を停止するために除去した枝を戻しているために起きているが,除去される枝はすべて循環の原因となる親子枝であり,この循環は婚姻関係グラフで障害ノードを切り離すカットセット枝をすべて除去しているのだから,循環枝を戻しても再び循環が起きることはないはずなのだが…

コード的には「多重婚配偶者は切り離す」という論理を追加しない方がシンプルであり,ある意味でこれで十分だとは思われるのだが,なぜエラーが発生するのかその理由だけは調べる必要がある.BuildSameGeneMarriageGraphのループのボトムでRetrieveRemovedEdgeを実行すると,今度はループが停止しなくなる.循環枝は9本で,多重配偶者を3件処理している.

  1. card=CARDLINK:#410 @3桐壷の更衣[0] wife=#408 桐壷院 @2
  2. card=CARDLINK:#554 @75葵の上[0] wife=#406 光源氏 @1
  3. card=CARDLINK:#656 @126源氏宮[0] wife=#488 朱雀院 @42

この後,【CARDLINK:#406 @1光源氏[0]】⇒【CARDLINK:#414 @5紫の上(若紫)[0]】の循環枝の処理に失敗してループしている.この親子枝はすでに一度処理されているものなのだが,なぜかぶり返している.光源氏は循環枝の親としてすでに2回登場しているので,光源氏の配偶者はすべて光源氏のブロックに移動しているはずなのだが,紫の上は光源氏の配偶者であると同時に養女でもあるので,配偶者として移動してもそのブロック内で自己ループになるということは考えられる.しかし,だとすれば,この手法を使わない場合でも同様のことが起きなくてはならないのだが,そうならないのはなぜか?

いや,逆に言うと,既定の方式でなぜ問題が解けているのか?という方が疑問だ.既定の方式では光源氏と紫の上は同じクラスタに残るはずであり,そのクラスタでもう一度自己ループが発生してもおかしくないのだが… できるものなら,この重婚クラスタ図をZTを使って表示してみたいのだが… 重婚クラスタ図は原理的に系統図とまったく同じなので,接続関係をZTに渡すことができれば描画は容易にできるはずだ.一つのアプリで2つの系図を同時に開くことができればそれも可能だが,現状では直ちには応じることはできない.

一覧表形式にして出力することは不可能だろうか?かなり面倒くさそうだが,不可能ではないと思う.CSVで出力できれば別アプリで開くこともできる.やってみる価値はあるとは思われるが,そのためにはかなりの準備が必要だ.いや,思ったより簡単なのではないか?重婚クラスタ図には親子関係しかないので,系図データとしてはかなり単純なものになるはずだ.この図面のノードはクラスタでそれに親子関係を与えたものがクラスタ図になる.ただし,この図面ではそのクラスタに含まれる結婚までは分からない.それをチェックするだけなら,むしろ既存のダンプルーチンで十分だ.

とりあえず,重婚クラスタグラフの連結成分リストをダンプすればよいのではないか?ループ出口では重婚クラスタグラフの連結成分リストは空になっている.むしろ,婚姻関係グラフの連結成分リストを見た方が早い.⇒光源氏と葵の上は同じクラスタ,紫の上,藤壷の宮などは孤立ノードになっている.以下のようなクラスタが存在する.

  1. 桐壷院,桐壷の更衣
  2. 朱雀院,源氏宮
  3. 式部卿宮,紫の上の母
  4. 明石入道,明石の尼君
  5. 紅梅,真木柱,蛍兵部卿宮
  6. 光源氏,葵の上
  7. 摂政太政大臣,大宮
  8. 柏木,落葉の宮,夕霧,雲井の雁
  9. 按察大納言(桐壷),桐壷の更衣の母
  10. 今上,明石中宮
  11. 匂宮,中姫君,匂宮の北の方,浮舟,薫,女二宮
  12. 冷泉院,秋好中宮,王女御,弘徽殿女御,冷泉院の御息所
  13. 按察の大納言(若紫),北山の尼君
  14. 先帝,先帝の后宮[0]
  15. 春宮,春宮の女御,麗景殿女御
  16. 二宮,二宮の北の方

意外に細分割されている.光源氏+紫の上の関係が切断されるのは,葵の上→夕霧という親子関係から,葵の上の配偶者光源氏の配偶関係が切断されることによって初めて発生する.従って,葵の上→夕霧という関係が存在していなければ同じ障害が発生する可能性はあるように思われる.従って,選別的に切断するのであれば,「多重婚配偶者」ではなく,むしろ「親子関係にある配偶者」ないし,「直系血族関係にある配偶者」を切断しなくてはならないということになるはずだ.

直系血族関係をチェックする既存関数としてIsAncestorOf,IsMyParentがある.前者は主語の下流系を探索し,後者は上流系を探索するもので,組み合わせればよさそうだが,前者はすべての人名リンクを探索しているので今の場合には適切ではない.後者は無効カードを除外しているが,特殊な場面で使うことを予定しているため応用できない.新たに一つ作ってみよう.⇒CARDLINK::IsMyAncestorという関数を新設した.IsAncestorOfとは逆に上流方向を探索するものになったが,紫の上を「直系血族配偶者を発見」として検出できている.

IsAncestorOfは現在使われていない関数だが,TestInevitableMultiZeroのすぐ下にあったところから推測して,重婚クラスタ検定の中で使う意図で開発されたものと思われる.クラスタの親子関係接続による循環を検査すればこと足りるということになって放置されていたのだろう.一応これで解決を得たが,このアルゴリズムで完全と言えるかどうか?については,確信が持てない.一番確実なのは,配偶者→配偶者の配偶者の枝をカットするのではなく,本人周辺の枝をカットして孤立ノードにしてしまうというのが一番確実なのかもしれないが…

祖父が孫と関係するなどのこともあり得るので,親子関係だけでは十分ではないということだけは確実だ.現行論理でも直系血族間で婚姻関係があれば,必ずクラスタ循環が発生するから検出は可能だが,現行のやり方では解けない場合が出てくる可能性がある.循環は垂直性の循環なのでそのどこを切っても循環は一応止まるが,解決にはなっていない.そのようなことが起こり得るということは上の例からも明らかだ.

現行では親ノードの配偶者をカットしているが,むしろ子どもの配偶者をチェックすべきではないだろうか?この方が確実にカットできるような気がする.親ノードの配偶者を周辺で切り取り,子ノードの直系血族配偶者をカットというのがわかり易いような気がする.⇒実装した.多分これが正解だと思う.循環枝を1本処理した段階で一度ループから離脱して検定を再実行するようにして,処理された循環枝3,削除された枝6に変化した.出力にも少し変化が現れた.

image

多重になっているのは,光源氏,藤壷の宮,柏木の3件だ.これらのカードが多重になっている原因は,光源氏の場合,紫の上が養女であるという直系血族との婚姻,藤壷の宮の場合は桐壷院・光源氏という親子との三角関係(俗に言う親子丼),柏木のケースは配偶者の落葉の宮と女三宮がいずれも重婚者,つまりダブル三角関係という同情の余地もない情況だ.図面も以前出ていたものとは大分トポロジーが変わっているが,今回の方がよりわかり易くなっているのではないかと思う.多分これが重婚クラスタ循環問題についての最終解となるだろう.

重婚クラスタ循環の多重を最小化する世代分割は可能か?

系図作図上の要点は多重カードの出現を極小化することにある.カード多重の原因が重婚クラスタ循環の存在にあることは2017年初頭にはすでに突き止められていたが,それから四年経過した今もなおパーフェクトな解法は得られていない.つまり,重婚クラスタ循環が存在しないときに多重カードゼロの図面を描画することはできているが,クラスタ循環が存在するときの多重カードの最小化には成功していない.と言うか,この部分は手つかずで放置されていた.今回はこの「系図作成上の最大の課題」に最終的な決着を付けたいと思う.

配偶関係(婚姻関係)によって相互に連結したカードの集合を重婚クラスタと呼ぶ.配偶関係は排他的な同値類を構成するので,ある重婚クラスタに属するカードが他のクラスタに属するということはあり得ない.サイズが2より大きいクラスタがあるとすれば,そこには必ず一人以上の重婚者がいると考えられる.重婚クラスタを節点とし,親子関係を枝とするグラフを重婚グラフBとする(クラスタCに属するカードPの子どもQがクラスタC’に入っている場合,C→C’の枝が生成される).親子関係には方向性があるので,グラフBは有向グラフであるとする.

グラフBが循環(閉路)を含んでいないときには,多重カードの存在しない図面が描画できることは明らかである.グラフBの連結成分を重婚クラスタ属とする.重婚クラスタ属とは親子関係によって垂直に連結された一群の重婚クラスタの集合である.有向グラフは任意の2節点間に有向経路が存在するとき,強連結であると言う.強連結グラフ上の任意の有向枝は必ずいずれかの有向閉路上にある.

※重婚クラスタ属とはZTシステムの用語で言えば「系統」に相当する.つまり,始系列と直接/間接に接合した複数の系列の集合と等価である.

有向グラフBの重婚クラスタ属Gが強連結であるか,強連結成分を含んでいる場合,つまり,クラスタ属Gが循環(閉路)を含んでいる場合には系図図面上には多重カードが発生することは避けられない.これを不可避の多重カードと言い,多重カード発生の要因となるようなクラスタ属に含まれる強連結成分(孤立ノードを除く,ただし自己ループを持つノードを含む)を重婚クラスタ循環と呼ぶ.

重婚クラスタ循環(循環を持つ重婚クラスタ属に含まれる強連結成分)から循環の要因となる親子枝を除去することにより重婚クラスタ属が循環しないようにすることは可能だが,一般に,このような枝(循環枝と称する)を除去しても事態は何ら好転しない.というのは,重婚クラスタ循環の要因となる「循環枝」はほとんどの場合,「自己ループ」であるからだ.と言うか,手持ちサンプルでは例外なく自己ループであるとしてもよいのではないかと思われる.

親子枝が自己ループになるというのは,ある重婚クラスタの中に親とその子どものカードが同時に含まれている場合に発生する.婚姻関係ないし配偶関係にある2つのカードがつねに同世代に配置されなくてはならないことと同様に,親子関係にある2つのカードでは少なくとも親カードは子どもカードよりも上の世代に配置されなくてはならないことは明らかだ.従って,親子枝が自己ループになるとすれば,この婚姻の水平的関係と親子の垂直的関係が直交する(両立しない)矛盾と言える.

重婚クラスタ内のノードは同一世代に配置することが求められているが,それが不可能であるとすれば,クラスタを一度世代別クラスタに分割する以外の方法はない.その方法を考えてみる.

  1. 重婚クラスタ循環から循環がなくなるまで親子枝(P, Q)を除去する
  2. 重婚クラスタ循環から除去された枝の集合を循環枝集合Sとする
  3. 重婚クラスタ循環に含まれるカードを節点とし,配偶関係を枝とする無向グラフを循環グラフJとする 仮定により,グラフJは連結であるが,内部に閉路を持っている場合もあり,持たない場合もある
  4. 枝集合Sから循環枝を1個取り出し,親子枝(P, Q)とする.
  5. ノードPないしQのいずれかがグラフJに含まれない場合はステップ8へ
  6. 枝(P, Q)の親ノードPと子ノードQを分離するようなグラフJのカットセット(切断枝集合)を求めて,グラフJから除去する
  7. 親ノードPを含むパートに含まれるノードをグラフJから除去する
  8. ステップ4に戻って枝集合Sが尽きるまで繰り返す
  9. ステップ5のカットセットに含まれるすべての枝を元の検定グラフ1から除去して,重婚クラスタ検定を再実行する

この解析の目標はステップ6のカットセットを最小化することだが,おそらく,除去されたカットセットの枝数と表示される多重カード数が(ほぼ)対応するのではないかと推定される.カットセットが最小であることを求めないのであれば,カットセットの枝集合を決めるのは簡単だ.親ノードPの周辺の枝をすべてカットすれば,Pは孤立ノードとなり,Qはそれ以外のすべてのノードと連結したままになる.その逆に,最小カットセットを求める問題はNP完全ではないかと思われる.たとえば,2つのパートの節点数が等しくなるようなk-カットセットが存在するか否かを問う問題はNP完全であることが知られている.※

※グラフの節点を2つのパートに任意に分割したときのカットセットを求めるのは難しくない.かき混ぜた納豆をスプーンにすくって取り分けるときに長く伸びた糸をくるくるっと回して切れば,それが「カットセット」であり,どのような分割でもカットセットは一意に決定できる.

次善の策として,ノードPから距離1だけ離れた枝を削除するということが考えられる.ノードPに隣接する枝はすべてPの配偶者だが,その次の枝はPの配偶者の配偶者への枝になる.事例から見ると重婚クラスタ循環の親子枝の親となるようなノードは通常重婚者であり,それも多重婚者である場合が多い.たとえば,桐壷院は配偶者を6持っているし,光源氏は13も持っている.従って,Pの隣接枝をカットするやり方では最小どころか,最大カットセットになりかねない.Pの配偶者が重婚者である可能性はゼロではないが,確率的にはそれほど高くない.

もし,この方式でよいとすれば,グラフJを新たに起こさなくても,元の検定グラフ1上で操作できるかもしれない.実装はそれほど難しくないので,試してみることにしよう.⇒うまくいったようだ.多重は3件まで減少した.まだいくつかエラーが出ているが,描画はほぼ完璧だ.

image

画面の下の方にあった多重は完全に解消した.下図は修正前のバージョンで描画したものだが,これまでは本来クラスタ循環と関わりのない部分で多量の多重が発生していた.これは重婚クラスタ循環で生じる世代的な矛盾が放置されていた(重婚クラスタ循環を世代分割していなかった)ためだ.今回はごく一部の不可避多重を除外してすべてのカードの世代が確定しているので,このような多重の発生する余地がない.

image

「婚姻関係で連結したカード集合」を「重婚クラスタ」と呼ぶことにする

これまで「重婚同類」と呼んできた「婚姻関係で連結したカード集合」を「重婚クラスタ」と呼んでみることにした.「セックスつながりのグループ」を俗になんと言っているのか分からないが,「重婚クラスタ」とはまさにそのような関係で連結したグループ以外のなにものでもない.薫のZ木家系図の登場人物は全36名だが,そのうち最大の重婚クラスタには23名ものメンバーが加わっている!重婚クラスタは「同値類」なので,あるクラスタに属するメンバーは他のクラスタとはまったく関わりがない.つまり,クリスプなクラスタである.※

※疫学的なクラスタの定義からはやや離れるが,「コロナ感染者」というグループを一つのクラスタであると考えると,世界中のコロナ感染者は明らかに世界でたった一つのクラスタに属している.コロナ前・コロナ後の世界が変わるというのはこのこと,つまり「世界は一つという事象の顕在化」を意味していると考えられる.

系図図面の縦軸は時間(世代)であり,婚姻関係とはある特殊な時間を共有することであると考えられるので,婚姻関係にあるカードは同世代に属するというのが系図作図上の基本原則である.しかし,世の中には世代差のあるカップルはいくらでも存在する.このようなケースでもその世代差を補助的な垂線(長い尻尾)で補うことによって(2つのカードを同世代に配置することにより)作図可能である.重婚クラスタを節点とし,親子関係を枝とするグラフ(重婚グラフ)を連結成分に分解すると,系図上で多重カードが発生するメカニズムが見えてくる.

重婚クラスタが親子関係によって多段に連結されていたとしても,それだけでは多重カードは発生しない.このような場合にはクラスタを階層的に配置することが可能であり,(同一クラスタに属する)同世代カードはBTWなどの技法によって(連結線を用いることで)単一のカードとして描画できる.多重カード発生の根本原因はこの階層化が循環によって不可能になることにある.(階層=木は閉路を含まない連結グラフである)従って,多重が発生するためには重婚グラフの連結成分が強連結であるか,ないし強連結成分を含むことが必要かつ十分な条件となる.

この条件には一つの重婚クラスタの内部に「親と子」が存在してグラフ的には自己ループを構成する場合を含んでいる.「重婚クラスタ」と言っても,その参加者がすべて「重婚者」であると言う訳ではない.たとえば,薫のZ木家系図のクラスタ23には単婚者が11人も含まれているが,そのうちの一人弘徽殿大后のカードは多重になっている.

重婚グラフは重婚クラスタをノードとし,親子関係を枝とするグラフだが,多重カードの最大の原因が重婚クラスタの内部親子関係であることは事例から明らかだ.ただし,現状では多重枝を登録しようとすると,「多重枝の登録は不可」が表示されて弾かれてしまうので,クラスタ23に関わる自己ループ枝は1つしか登録することができない.このために,重婚クラスタ循環=1ということになっているのだが,これは実態を反映していない.⇒重婚グラフの属性をMULTIEDGEGRAPH(多重枝グラフ)に設定して動作を見ることにした.

薫Z木家系図の重婚グラフには親子枝が23個登録され,このうち10個がクラスタ23に関わる循環枝(自己ループ)であることがわかった.

  1. 【#408 @2桐壷院】⇒【#406 @1光源氏】
  2. 【#408 @2桐壷院】⇒【#488 @42朱雀院】
  3. 【#410 @3桐壷の更衣】⇒【#406 @1光源氏】
  4. 【#488 @42朱雀院】⇒【#514 @55落葉の宮】
  5. 【#488 @42朱雀院】⇒【#518 @57女三宮】
  6. 【#556 @76致仕太政大臣】⇒【#562 @79柏木】
  7. 【#624 @110弘徽殿大后】⇒【#488 @42朱雀院】
  8. 【#656 @126源氏宮】⇒【#518 @57女三宮】
  9. 【#674 @135致仕太政大臣の北の方】⇒【#562 @79柏木】
  10. 【#710 @153一条御息所】⇒【#514 @55落葉の宮】

これはかなり予想外の結果だ.これらの枝をすべて除去しないと,この系図の重婚クラスタ循環は解消しない.そのことを考えると薫のZ木家系図で多重カードが3件しか出ていないというのはむしろ驚異と言ってもよい.薫のZ木家系図を再掲してみよう.

image

おそらく,この図面で多重カード3というのは極限と思われるが,上の循環枝リストからは3という数字を導出することはできない.考えられるファクタとしては,「世代数」が関わっている可能性だ.このクラスタに関わるノードをすべて色表示してみよう.

image

クラスタ23に関わる23のカードは3世代の範囲に分布している.このことから推定されるのは,おそらく「クラスタが分布するk世代の範囲に相当する多重カードkの出現は避けられない」ということではないだろうか?最適解と言えるかどうかは分からないが,各世代に高々1枚の多重カードというのは望み得る限界ではないかと思われる.この循環する親子枝10本を解析すれば,このクラスタを展開するのに少なくとも3世代を要するということを立証できるのではないだろうか?もし,うまくゆけば,メンバーをどのように世代配分すれば最適かというところまで分かるかもしれない… つまり,本来なら一世代に収まらなくてはならない重婚クラスタを「世代分割」可能か?という問題として定式化できる.

それを調べるには,やはり次の段階であるハッセ図と呼ばれる階層図の生成手順を見るしかないだろう.ハッセ図(多重グラフ3)は重婚グラフ(多重グラフ2)をベースに構築される.もし,その手続きの中で「重婚クラスタの(多重を最小化する)世代分割」が可能であれば,図面はほぼ仕上がったものと言ってよい.つまり,(多重不可避のカード以外の)すべてのカードの絶対世代番号が確定できるということになる.もちろん,多重が存在しない場合にはすでに目標は達成されている.問題は重婚クラスタ循環が存在する場合にそれが可能か?という点にある.

重婚同類循環数=重婚同類循環を切断するために削除された枝数とする

重婚同類循環の見直しに掛かっているところだが,先に進む前にまず,最近の修正をフィックスしてから取り掛かることにしよう.仕掛りのオプションには以下がある.すべて現状でフィックスすることにする.

  1. majortribepathを廃止する@20210115 7箇所
  2. 異世代ではBTWは成立しない@20210115 廃止
  3. setMajortribeを使う@20210115 4箇所
  4. MakeTooYoungWifeでSTOPCARDSHIFTを無視@20210116 1箇所
  5. 系列木グラフを連結成分に分解する@20210115 4箇所
  6. DONTSORTTRIBEを廃止する@20210116 廃止
  7. AlternateTribeRealNodeを廃止する@20210116 2箇所
  8. adjustGenerationRangeを停止する@20210117 1箇所 ⇒ 保留
  9. ChangeRealRefferenceで系列優先ノードの切り替えは実施しない@20210119 1箇所
  10. TailSheddingで差分が負の場合はノード対を破棄する@20210120 1箇所
  11. sameGeneCyclesのカウントを整理する@20210124 6箇所
  12. removeDeadEndBranchを廃止する@20210124 3箇所
  13. 重婚グラフの自己ループ枝を削除しない@20210124 6箇所

仮修正6箇所,暫定修正7箇所.

源氏物語全系譜6.1.ZELの傍系血族図を#4 明石中宮で開いて,TestInevitableMultiZeroでエラーが発生する.TestStronglyConnectedで循環が検出されて停止した後,MakeHasseDiagramで無限ループしてスタックオーバーフローが起きる.⇒上記でremoveDeadEndBranchを廃止しているが,この修正は誤っている.removeDeadEndBranchは「行き止まりノードの枝をグラフから削除」して閉路上の枝だけを残すための関数で,同種の関数が三種ある.

①RemoveDeadEndBranch,②removeDeadEndBranch,③RemoveSingleEndBranch,うち①は軸線図グラフ専用で,②はそれを汎用化したもの,③は「一端が一本の入力枝ないし出力枝しか持たないような枝を削除する」ための関数で,最終的には閉路上の枝だけが残るというものだが,②と③を等価な関数と早とちりしてしまった.実際には,③は対象が無向グラフでなくては動作しない.⇒removeDeadEndBranchを復活させ,①をRemoveDeadJikusenBranchにリネームして,RemoveDeadEndBranchの名前は②が引き継ぐようにした.

これで障害は解消したが,TestInevitableMultiZeroでTestStronglyConnectedの戻り値が非ゼロでハッセ図が循環しているというエラーになった.これは削除した枝を戻すとき自己ループ枝しか保留していなかったためだ.重婚同類循環に関わるすべての枝(重婚同類循環を切断するために削除されたすべての枝)を保留するようにしてエラーは解消した.これに伴い,SameGeneMarriageCirculationをBuildSameGeneMarriageGraphに吸収して一本化し,さらに,TestInevitableMultiZeroの一部もBuildSameGeneMarriageGraphに移動して,(1)婚姻/重婚グラフと(2)ハッセ図の境界を明確にした.

sameGeneCycles(重婚同類循環数)の値は「重婚同類循環を切断するために削除された枝数」と再定義する.

▲同上サンプルの全体図を#101 朧月夜で開いて,NAMEBOX:IsPossibleBTWLeftHandで停止する.「右手配偶者が左手本人」というエラーが起きている.エラーを無視して描画は可能.障害ノードはNAMEBOX #1826 髭黒(2)で,右手結婚枠はMARGBOX #1182:#1254 玉鬘(0)+#1373 髭黒(0)→#1266 左兵衛督(0),右手配偶者はNAMEBOX #1373 髭黒(0).髭黒(0)を左手本人とするBTW右手結婚枠はMARGBOX #1206:#1391 髭黒の前の北の方(0)+#1809 髭黒(1)→#1810 真木柱(2).

出力を見ると,髭黒+玉鬘のBTWは成立しているが,髭黒+髭黒の前の北の方では連結線が引かれていない.髭黒と髭黒の前の北の方は同世代なので連結線を引くことは可能であるように見えるのだが… FALSEで復帰するようにすると,髭黒は玉鬘の隣に配偶者として配置され,髭黒の前の北の方とは連結線で結ばれるが,本人カードが別の位置に配置され,他の配偶者(木工の君,中将のお許)との結婚はその位置で展開される.「右手配偶者が左手本人」をエラーとしないという方向でよいと思われるが,ここでは保留としておく.この件は「右手配偶者が左手本人で停止しない@20210108」というDEBUGオプションになっている.

同上サンプルの直系親族図 基準ノード=#49 八宮でNAMEBOX:RestoreExtractBoxのMargPointOffsetエラーが発生した.エラーを無視して描画は可能.MARGBOX::CheckMargBoxChanged→ IfMargboxVisible→ ConfirmGoldenCouple→ GetGoldenCouple→ RestoreErasedYoungWifeが実行されている.MargPointOffsetの移動量は-1なので誤差の範囲と考えられる.

image

完全木テストが完了した.

image

前回より少し速くなっている.だいぶ重婚同類循環の様子が見えてきた.前出のサンプル:薫のZ木家系図の場合で言うと,重婚同類循環は1つで,これに関わっているノードは23点ある.

  1. Corder=3 wives=13 #406 光源氏 @1
  2. Corder=3 wives=6 #408 桐壷院 @2
  3. Corder=3 wives=1 #410 桐壷の更衣 @3
  4. Corder=3 wives=5 #488 朱雀院 @42
  5. Corder=3 wives=2 #514 落葉の宮 @55
  6. Corder=3 wives=2 #518 女三宮 @57
  7. Corder=3 wives=5 #556 致仕太政大臣 @76
  8. Corder=3 wives=2 #562 柏木 @79
  9. Corder=3 wives=2 #600 夕顔 @98
  10. Corder=3 wives=2 #604 源典侍 @100
  11. Corder=3 wives=2 #606 朧月夜 @101
  12. Corder=3 wives=2 #612 藤壷の宮 @104
  13. Corder=3 wives=1 #624 弘徽殿大后 @110
  14. Corder=3 wives=1 #628 麗景殿女御 (桐壷院の)@112
  15. Corder=3 wives=1 #644 八宮の母女御 @120
  16. Corder=3 wives=1 #656 源氏宮 @126
  17. Corder=3 wives=1 #670 麗景殿女御 (朱雀院の)@133
  18. Corder=3 wives=1 #674 致仕太政大臣の北の方 @135
  19. Corder=3 wives=1 #708 承香殿女御 (朱雀院の)@152
  20. Corder=3 wives=1 #710 一条御息所 @153
  21. Corder=3 wives=2 #752 雲井の雁の母 @174
  22. Corder=3 wives=1 #838 承香殿女御 (桐壷院の)@217
  23. Corder=3 wives=1 #856 近江の君の母 @226

婚歴を見ると,光源氏13, 桐壷院6, 朱雀院5, 致仕太政大臣5を筆頭に,落葉の宮,女三宮,柏木,夕顔,源典侍,朧月夜,藤壷の宮,雲井の雁の母の2が並んでいる.しかし,これだけでは循環にはならない.これに,桐壷院→光源氏,弘徽殿大后→朱雀院,致仕太政大臣→柏木などの親子関係が(自己ループとして)加わってようやく重婚同類循環が完成する.「重婚同類」と呼んでいるのは,「重婚関係」で連結したグループが排他的な「同値類」を構成するためだ.今後は重婚同類と言う代わりに「重婚クラスター」と呼んでみることにしよう.

「重婚同類循環は1つ」という言い方をしているが,この表現はあまり正確ではない.重婚グラフから抽出した強連結成分を分解するのに必要な最小枝数を重婚同類循環数としているが,現在扱っている枝グラフは多重枝を認めていないため,自己ループが1個しか登録されていないことで最小枝数1ということになっているので,多重枝を扱えるようになると,たとえば,今のサンプルの場合なら少なくとも自己ループ枝は3つになるはずだ.従って,「重婚同類循環の個数」と「重婚同類循環を分解する最小枝数」ははっきりと区別する必要がある.

多重カード出現の極小化

現行ではすでに垂直セグメント検定(TestVerticalSegment)という処理は廃止(2018/02/26)されているので,垂直スプリットが発生したときそれを補正する手段は存在しないが,AFTERMARGSAMEGENEフェーズでShiftDirectAbsoluteの実行を抑制すれば垂直スプリットの発生を回避することはできる.これは重婚同類検定ですべてのノードの垂直位置(世代)を事前に決定しているためと考えられる.しかし,循環性多重が存在するときには重婚同類検定の結果をまったく使えないというのも過剰であるように思われる.重婚同類検定をもう少し深堀りして,「寸止め」のところまでは実行できるようにしたい.

目的は「多重カード出現の極小化」にある.実際,昨日の反例サンプルを見ると,少なくとも2枚の多重カードは削減できるように思われる.多重カードの削減はメインループの中でも動的にTOPOLOGY:ReduceMultiCardを使って実施しているはずなのだが… なぜそれが利かないのか調べてみたところ,多重カード数が不可避の多重カード数と同じになるとブレークしていることがわかった.この「不可避の多重カード数」を強制的に-2してループするようにして,下図を得た.

image

多重カード数は5から3に減少している.重婚同類循環の件数は3なので明らかにこの図面がこれ以上多重カードを削減できない極限に達していることは明らかだ.つまり,この図面は少なくともこのサンプルに限って言えば,「完成図」であると言える.TribeRelocationのステージ【7.8】多重カードゼロを検査して変化なしなら検定打ち切りの打ち切り条件を多重不可避(Inevitables)から重婚同類循環(sameGeneCycles)に変えることでこの動作を一般化できる.しかし,これだけでは多重を極小化することはできない.

たとえば,光源氏の全体図では重婚同類=3であるにも関わらず,多重カードは16も残ってしまう.このとき,多重不可避は8となっている.まず,これらの用語の意味をもう一度確認しておこう.重婚同類循環(sameGeneCycles)はTestInevitableMultiZeroが返す値で,重婚同類循環の発生件数と考えられるが,かなりルーズな扱いを受けているのでもう少し整理する必要がある.sameGeneCyclesは以下のポイントでインクリメントないしセット/リセットされている.

  1. TOPOLOGY::ResetExperiment 初期化
  2. TRIBERELOCATIONフェーズ入口 リセット
  3. SIMPLENODE::MakeOyakoEda(多重グラフ2) 
    自己ループでカウントアップ
  4. BuildSameGeneMarriageGraph(多重グラフ2)の結果を加算
  5. SameGeneMarriageCirculationの結果を加算
  6. TOPOLOGY::TestInevitableMultiZero 
    除去したループ枝の個数をカウントアップ
  7. TestInevitableMultiZeroの結果をセット

どうもあまりわかり易くない.重婚同類検定では枝グラフを3つ使っているのだが,それぞれの構成/用途を確認しておこう.

  1. 多重グラフ1(tajugraph1) 節点:人名カード,枝:婚姻関係(単身婚の場合は自己ループ),複数親を持ち子どもを持たない終端ノードの自己ループ枝,連結成分:婚姻関係によって連結するカード集合
  2. 多重グラフ2(tajugraph2) 節点:重婚同類(グラフ1の連結成分),枝:親子関係(親の含まれる連結成分と子の含まれる連結成分を結ぶ)自己ループを削除しsameGeneCyclesに加算する,冗長枝を除去する 連結成分:親子関係によって連結する重婚同類の集合(重婚同類族)
  3. 多重グラフ3(tajugraph3) 節点:グラフ2の連結成分,枝:親子関係(グラフ2の枝)

これもまた,あまりわかり易いものではない.特に多重グラフ3と2の違いがよく分からない.グラフ3の枝がグラフ2の親子関係であるとすれば,すべての枝が自己ループになってしまうような気がするのだが… ここではグラフ3 をハッセ図と呼んでいるようだが,その意味も不明だ. SIMPLEGRAPH:TightenHasseDiagram では出口でグラフ2の除去された枝をすべて元に戻している.BuildTightHasseDiagramでは静定するまでこの関数を呼び出しているのだが…

しかし,この部分を読み解かないことには始まらない.グラフ1を婚姻グラフ,グラフ2を重婚グラフ,グラフ3をハッセ図と呼んでおくことにしよう.1の婚姻グラフに2種の自己ループ枝が追加されている理由が分からない.重婚同類を見るためには婚姻関係と親子関係の両方を見る必要はあるが,なぜ単身婚を加える必要があるのか?世代を確定するためには必要になってくるという可能性も考えられるが…

2の重婚グラフの目的は重婚同類循環(sameGeneCycle)の個数を確定することにあると考えてよいだろう.しかし,カウントを持ち寄って合算するような判り難いものになっている.3のハッセ図の目的は多重を除去した状態で絶対世代番号を確定しようとしているものと解釈されるが,毎回枝を除去してまた戻すという操作の意味がよく分からない.

ハッセ図はとりあえず置いて,重婚同類循環(sameGeneCycle)を確定することを考えよう.重婚グラフのノードである重婚同類が親子関係としての自己ループを持つとすれば,重婚同類循環に当たることは明らかだが,親子関係によって連結した連結成分要素である重婚同類族それ自体はただちには重婚同類循環には当たらない.それをどこで見ているのかをチェックしておこう.

最初にsameGeneCyclesをカウントアップしているのは,BuildSameGeneMarriageGraphだが,ここの処理で分かりづらいのは,この関数の内部ですでにsameGeneCyclesを一部カウントアップしているという点だ.これは改めるべきだろう.この関数はカウンタloopedgeの値を返しているが,AddOyakoEdaの中ですでにsameGeneCyclesをインクリメントしている.SIMPLENODE:AddOyakoEda→MakeOyakoEdaでは自己ループになったときにはその枝のselfloopフラグをオンにしている.

BuildSameGeneMarriageGraphではこの後,自己ループ枝を削除するときにloopedgeをインクリメントしているので,重複カウントされている可能性がある.⇒この修正で重婚同類個数は2になったが,多重カード3が残る.メインループはトポロジーが変化しなくなると多重が残っていてもループを抜けるようになっている.見たところいずれも「不可避の多重」のように見える.重婚同類循環が2つであるとすれば,その構成要素を特定できなくてはならない.重婚同類循環の検査はSameGeneMarriageCirculationで実施している.しかし,この検査では循環は検出されていない.SameGeneMarriageCirculationではTestStronglyConnectedでグラフを「強連結成分に分解」している.

サンプルとして用いている薫のZ木家系図では重婚同類循環数は2となっている.一つは自己ループ(桐壷院→光源氏)だが,もうひとつはなんだろう?TestStronglyConnectedは3箇所で使われている.一つはSameGeneMarriageCirculationの中であとの2箇所はTOPOLOGY:TestInevitableMultiZeroだが,いずれも無反応だ.ただし,TestInevitableMultiZeroではtajugraph2->selfloopの値をsameGeneCyclesに追加している.これはおそらく前にカウントした値が残留したものと推定されるので,おそらくカウントの重複と思われる.selfloopcountで数え直ししてみることにしよう.

自己ループが1残っている.ただし,これはすでに削除された枝だ.削除と言っても現物はそのまま,「削除された」というマークが付いているというだけだが,selfloopcountはマークを無視してカウントしている.このルーチンを修正して,自己ループカウントはゼロになったが,今度はTestInevitableMultiZeroのステージ【14】強連結成分の抽出で削除した枝を戻すの段でカウント不一致が発生するようになってしまった.この関数は元の仕様に戻した方がよさそうだ.その代わり,sameGeneCycleにloopedgeを加算ではなく,代入とするのが正しい.

結局,重婚同類循環=1で多重カード3という結果になった.しかし,これでもまだあいまいさが残る.重婚グラフの枝集合に含まれる自己ループは重婚同類循環に関わるものだが,必ずしも一対一という訳ではない.自己ループ以外の循環はSIMPLEGRAPH::TestStronglyConnectedで検査しているが,この検査では「行き止まりノードの枝をグラフから削除する」ということを実施しているだけで,もし枝が残ればそれは重婚同類循環に含まれる枝ということになるが,この場合はサイクルを構成する枝集合と重婚同類循環が対応するということになり,やはり一対一の対応にはなっていない.整理すると以下のようなことになる.

  1. 重婚グラフの枝集合の中の自己ループ枝は重婚同類循環に関係する
  2. TestStronglyConnectedで除去されなかった枝は同類循環に関係する

ということになり,重婚グラフの枝集合を見ても確定的な情報は得られない.連結成分はノードの集合であり,それらがどの枝に関わっているかということは直ちには分明ではない.TestInevitableMultiZeroの出口で重婚グラフをダンプすると,節点数=6 枝数=7 有効枝=6 removed=1 nouse=0 usable=0 selfloopcount=1という結果になった.接点数6というのは重婚同類が6個あることを意味する.枝数7でうち有効枝数6というのはremovdeが1あるためだろう.selfloopcount=1というのは枝リストをスキャンして得た値だろう.7つの枝のうち,removedとselfloopのものが1つある.

SIMPLEEDGEには,selfloop,removedの他に,usableというメンバー変数がある.これには閉路上の枝というコメントが付いているので,これを利用することにしよう.いや,この変数は「有効枝」という意味で使われているようだ.今の場合はこれと真逆な使い方になる.「冗長枝」という意味で使われることもあるようだ.この他にnouseという変数もある.これには「切断された枝」というコメントが付いている.removedとどういう違いがあるのだろう?nouseというのは連結成分への分解で使われているようだ.

SIMPLEEDGEにcircular // 循環する枝(閉路上の枝)という変数を追加した.また,SIMPLEGRAPHにcirculars // 循環する枝数(閉路上の枝,自己ループを含む)を追加した.

InvestigateVSplits 垂直スプリット発生

源氏物語全系譜6.1.ZELのZ木家系図 基準ノード=#74 薫の出口検査でTREEVIEW::InvestigateVSplits 垂直スプリット発生

image

以前は「垂直セグメント検定」というのが実装されていたのだが,現在は完全に廃止されていて,それに代わるものは存在しない.20.AFTERMARGSAMEGENEで多重が発生しているとき,ShiftDirectAbsoluteとAdjustTribeGenerationを実行しないようにすればこの障害は回避できる.

image

この設定で源氏の完全木テストを実施してみた.

image

これまででもっとも速い時間で終わった.しかし,これで終わりという訳にはゆかない.この図面には多重が5件発生しているが,もう少し削減できるはずだ.その解を求めるためにはもう少し重婚同類検定を深堀りする必要がある.つまり,循環性多重が存在する場合でもShiftDirectAbsoluteを実行するという方向を追求してみたい.

ダミー枠チェーン上の結婚枠直列検査

源氏物語全系譜6.1.ZELの傍系親族図を#8 夕霧で開いて,MARGBOX:GetUpperNodeで停止する.TYW枠 #2396:#2438 弘徽殿女御(3)+→#1704 弘徽殿女御(1)の直上に親ノードの#2438 弘徽殿女御(3)が存在しないという状態になっている.これは,#2347 紅梅(1)でTailSheddingを実行したとき,紅梅のTYW枠#2348:#2541 紅梅(2)+→#1328 紅梅(0)を一旦取り外す操作に弘徽殿女御のスレッドを巻き込んでしまっているためと推定される.

TailSheddingでは,長い尻尾の末尾に接続するTYW枠を対象に取り出しを実施しているが,このケースのように複数のTWY枠が接続していることを想定していない.⇒いや,スレッド自体は区別されている.長い尻尾は独自のldrchainによって独立したチェーンを構成しているから,取り出そうとしているTYW枠自体は間違っていない.youngwife->CutBranch(false)の操作で誤っているのではないだろうか?⇒いや,CutBranchではまだ誤操作されていない.その次のyoungwife->Disconnectではないか?いや,ここでも問題は生じていない.むしろ,TYW枠をつなぎ戻すところでやり損なっているのではないだろうか?MakeLongTailが元凶であるように思われる.

NAMEBOX::MakeLongTail→ makeLongTailは「LDR束代表ノードが所有するLDR束のデータ整合性をチェックする」という関数だ.makeLongTailの中で不良が検出されている.不良スレッド thread=NAMEBOX #2395 弘徽殿女御(2) この処理ではMakeLongTailの戻り値はチェックされていないが,現在の不具合と関係がある可能性はある.⇒暫定的にエラーで停止するようにしておいたが,このエラーは座標に関するもので現在の問題とは直接関わっていないような気がする.問題はMakeLongTailではTYW枠の存在がほとんど無視されているように思われる点だ.不良はmakeLongTail→ CheckDummyBox→ CheckDummyBoxSerialで起きている.

この関数は「代表LDRノードのダミー枠チェーン上のすべてのダミー枠につきダミー枠直列を検査」している.ダミー枠直列というのは,結婚枠がYリスト上で直列に並んでしまう事象を指すものと思われる.これは拡張子ども枠などの場合に起こり得る現象だ.「ダミー枠直列」として,NAMEBOX #2347 紅梅(1) のスレッドで,MARGBOX #2435:#2347 紅梅(1)+→#2438 弘徽殿女御(3)とMARGBOX #2348:#2347 紅梅(1)+→#1328 紅梅(0)が直列であることが検出され,その結婚枠がTYW枠である場合には,それをYリストから切り出した後,長い尻尾の末尾に繋ぎ変えている.

image

紅梅(1)はLDR束の代表ノードでdummyboxにはMARGBOX #2435:#2347 紅梅(1)+→#2438 弘徽殿女御(3)が入っている.このダミー枠にはNAMEBOX #2438 弘徽殿女御(3)しか入っていない.従って,紅梅(1)の長い尻尾終端は弘徽殿女御(3)ということになり,TYW枠MARGBOX #2348:#2347 紅梅(1)+→#1328 紅梅(0)はこのノードの後ろに挿入されるため,#2396 MARGBOX→ #2348MARGBOX→ #2436 NAMEBOXという並びになってしまう.

MARGBOX #2435とMARGBOX #2348が直列になるというのは確かにおかしいが,今の場合#2348は紅梅(1)に直結されなくてはならないはずだ.しかし,TYW枠というのはダミー枠には勘定されないから,MARGBOX #2348の居場所がないということになる.これは想定外の構図なのだろうか?紅梅(2)が削除される前の状態をもう一度チェックしてみよう.最初の図式では紅梅(2)と弘徽殿女御(3)はともに終端ノードなのでその先にTYWをぶら下げても矛盾は生じないが,親元結婚枠内のLDRに直結するTYW枠の場合,ダミー枠と直列構造になることは避けられない.問題はこのときの移動先が間違っているということではないだろうか?ダミーノードはldrchainでリンクされているが,LDRからダミーノードへのリンクがかなりあいまいになっているような気がする.

LDR→ TYW枠はtooyoungで参照できるが,LDR→ダミーノードへの参照が存在しない.これは世代差ゼロのTYW枠,つまり,ZTYW枠とはまた別の話だ.⇒いや,少し誤解している.LDRもldrchainを使っている.TYW枠を持つLDRのldrchainが空であるということは,そのノードが長い尻尾の末尾であることを示している.このノードが代表ノードであるときには,ダミー枠直列というのはノーマルと考えてよいはずだ.⇒CheckDummyBoxSerialで直列枠がTYW枠のときは,GetTYWElderでELDERWIFEを取り出し,longtailの末尾を取り出すようにした.

同上サンプルの傍系親族図を#151 左大臣の姫君でソートして,NAMEBOX::makePairBoxで停止した.NOCOMMON属性未設定がPAIRBOX #2049:#2046 玉鬘(2)→#1254(0)で起きている.対象ノードはNAMEBOX #1596 秋好中宮(1).このエラーを補正するにはPAIRBOX::CheckNoCommonEndPointを実行しなくてはならないが,CheckNoCommonEndPointはNAMEBOX::makePairBoxでノード対生成時に一度チェックされる他は,PAIRLIST::RepairPairBoxが回ってこないと補正されない.従って,秋好中宮(1)のノード対生成時にこの事実が判明していないのは避けられない.

いや,端点共有があるときは,SwapBundledPairが実行されている.この中で検査していないということは考え辛い.⇒SwapBundledPairの中でノード対のNOCOMMONPAIR属性をチェックするのではなく,直接CheckNoCommonEndPointを実行するようにした.

同上サンプルの法定親族図を#104 藤壷の宮でソートして,PAIRBOX:RepairCommonEndPointで停止した.端点一致でsamepointゼロエラーが,PAIRBOX #2072:#2067 弘徽殿女御(2)→#1329(0)で起きている.対象ノード対はPAIRBOX #2072:#2067 弘徽殿女御(2)→#1329(0).このノード対は「端点一致でsamepointゼロ」という理由でRepairCommonEndPointを実行しているのに,状態が変化していない.

RepairPairBoxでチャンネルの付け替えが実施され,チャンネル0→2に移動している.このあと,MoveSamePointでチャンネル0への移動が実施されるが,この関数の中で「最大区間でないnocommonを別チャンネルに移動」が発生し,PAIRBOX #2028:#2025 玉鬘(2)→#1254(0)が別チャンネルに移動している.このとき,弘徽殿女御のノード対も一緒に移動しているものと見られる.

MoveSamePointでSwapBundledPairを実行するまではsamepointは値を持っているが,最大区間が逆転しているため切り離されて元の状態に戻っている.これは,端点共有と認定しているところで間違っていると考えられる.少なくとも共有端点ノード対がNOCOMMONであるか否か,もし,NOCOMMONならノード対の区間と比較する必要がある.

PAIRBOX::searchCommonPairが誤認定していると見るべきではないか?⇒Bobject::getCommonPairTypeではNOCOMMONPAIRクリティカル属性を見るのではなく,直接CheckNoCommonEndPointを実行するようにした.また,getCommonPairTypeの呼び出し側では,実ノードのNoCommonGoodSonを実行するか,ないしCheckNoCommonEndPointを実行するようにした.

image

▲同上サンプルのZ木家系図 基準ノード=#74 薫の出口検査でTREEVIEW::InvestigateVSplits 垂直スプリット発生

TailSheddingで差分が負のときはノード対を破棄する

源氏物語全系譜6.1.ZELの直系親族図を#57 女三宮でソートして,TRIBELIST::ShiftDirectAbsolute→ NAMEBOX:TailSheddingで停止.TailSheddingはトカゲの尻尾切り(tail shedding)のように長い尻尾を切り詰める操作だ.仮ノードと実ノードの世代差をD,長い尻尾の長さをLとしてdiff = L-Dだけ短縮されるが,diffが負の場合は操作できないためエラーになる.ShiftDirectAbsoluteではカードを絶対世代番号で指定された位置にシフトして重婚同類検定の結果を図面に反映させようとしているが,重婚同類循環が発生している場合には絶対世代番号を一意に決定することができないため,矛盾が生じているものと考えられる.

従来仕様では重婚同類循環が検出された場合にはShiftDirectAbsoluteを実行しないようになっていたのだが,可能な限り適用するというのが現在の方針だ.,まず,diffが負のときには無動作で抜けるように修正してみよう.⇒停止しないで描画できるようになった.このような事象は光源氏(2)と朱雀院(1)で起きている.TailSheddingでは差分がゼロのときには「LDRノード対の世代差がゼロになったときは仮ノード消去に転換する」という処理が組み込まれている.

また,TOPOLOGY::CheckShiftedPairBoxでは差分が負の場合はノード対を破棄するという処理を実行しているので,この論理をTailShedding本体に組み入れてみよう.⇒出力はかなり変わった.光源氏(0)と朱雀院(0)の頭に入っていた連結線が消え,桐壷の更衣→光源氏,弘徽殿大后→朱雀院が直結するようになった.

image

図面的にはこの方がわかり易い.修正前の図面を出してみよう.

image

コンポーネントは同一で,それらが水平シフトしているだけの違いだが,(修正後の方が)変に込み入ったところがないのがよいと思う.

同上サンプルの傍系親族図を#6 玉鬘でソートしてTRIBEBOX:SetMinorTribeで停止した.系列優先実ノードと優先仮ノードの実ノードが異なるというエラーだ.⇒NAMEBOX:DoublyBlessedOneでOnDoublyBlessedOneである区間を延長して,ChangeTribeRealnodeを実行しているブロックを含むようにした.また,これと付随して,hasPhysicalConnectionでこれまで優先仮ノードが(PRIMGHOST | ELDERWIFE))属性を持つ場合はつねに物理コネクション成立としていたのを,「実ノード不一致」と「実ノード属性不正」では不成立とした.

▲同上サンプルの傍系親族図を#8 夕霧でソートしてMARGBOX:GetUpperNodeで停止した.MakePairListClean実行中,CheckPassingPoint→ NoCommonGoodSon→ GetUpperNodeで起きている.障害が起きているのはMARGBOX #2396:#2438 弘徽殿女御(3)+→#1704 弘徽殿女御(1)で,TOOYOUNGWIFE|MOTHERLESS属性とHASNOMARGLINKクリティカル属性を持っている.

HASNOMARGLINKは「結婚リンクに接続していない結婚枠」の意味で,#2438 弘徽殿女御(3)に接続している.しかし,この結婚枠の上流には弘徽殿女御(3)は出てこない.このパスは#1324 致仕太政大臣(0)まで続いているが,弘徽殿女御(3)はその経路上に現れない.

この結婚枠の上にはMARGBOX #2348:#2541 紅梅(2)+→#1328 紅梅(0)があり,その上のノードはNAMEBOX #2541 紅梅(2)だ.MARGBOX #2348もMARGBOX #2396とまったく同じ属性を持っているのでTYW枠と考えられる.紅梅(2)はダミーノードでその上にMARGBOX #2435:#1700 柏木(2)+→#2541 紅梅(2)があり,その上がNAMEBOX #2347 紅梅(1) でEXTRACTBOX|ELDERWIFEになる.#2438 弘徽殿女御(3)の長い尻尾をDumpLongTail で見ると,

NAMEBOX::DumpLongTail >>>>>>>>>> #2438 弘徽殿女御(3)
   1  dum#=1 #1700 柏木(2)
   1  dum#=2 #2347 紅梅(1)
   2  dum#=2 #2541 紅梅(2)
   1  dum#=3 #2395 弘徽殿女御(2)
   2  dum#=3 #2438 弘徽殿女御(3)
NAMEBOX::DumpDummyChain >>>>>>>>>>>
   1  thread #1700 柏木(2) #2347 紅梅(1) #2395 弘徽殿女御(2)
   2  #2435 [結婚枠]:[4] #2541 紅梅(2) #2438 弘徽殿女御(3)
NAMEBOX::DumpLongTail <<<<<<<<<<<<

のような3本のスレッドからなるLDR束になっている.#2541 紅梅(2) と#2438 弘徽殿女御(3)は一つのダミー枠に入っているので,その下位のMARGBOX #2396が上位の#2541 紅梅(2)を参照しているというのはノーマルなのではないだろうか?#2438 弘徽殿女御(3)はDUMMYBOX|BUNDLEDSTRINGという属性を持っている.この結婚枠のownerは弘徽殿女御(0)だ.

障害はNAMEBOX::TailSheddingで発生している.NAMEBOX #2347 紅梅(1)の長い尻尾の始末を付ける段で,同じLDR束に入っている#2438 弘徽殿女御(3)が影響を受けているものと見られる.処理開始前の状態は下記のようになっている.

NAMEBOX::DumpLongTail NAMEBOX::TailShedding >>>>>>>>>> #2347 紅梅(1)
   1  dum#=1 #1700 柏木(2)
   1  dum#=2 #2347 紅梅(1)
   1  dum#=3 #2395 弘徽殿女御(2)
   2  dum#=3 #2438 弘徽殿女御(3)
NAMEBOX::DumpDummyChain NAMEBOX::TailShedding >>>>>
   1  thread #1700 柏木(2) #2347 紅梅(1) #2395 弘徽殿女御(2)
   2  #2435 [結婚枠]:[4] #2438 弘徽殿女御(3)
NAMEBOX::DumpLongTail NAMEBOX::TailShedding <<<<<

TailShedding実施後は下記のようになっている.

NAMEBOX::DumpLongTail NAMEBOX::TailShedding >>>>> #2347 紅梅(1)
   1  dum#=1 #1700 柏木(2)
   1  dum#=2 #2347 紅梅(1)
   2  dum#=2 #2541 紅梅(2)
   1  dum#=3 #2395 弘徽殿女御(2)
   2  dum#=3 #2438 弘徽殿女御(3)
NAMEBOX::DumpDummyChain NAMEBOX::TailShedding >>>>>>
   1  thread #1700 柏木(2) #2347 紅梅(1) #2395 弘徽殿女御(2)
   2  #2435 [結婚枠]:[4] #2541 紅梅(2) #2438 弘徽殿女御(3)

結局,紅梅の長い尻尾が1伸びて,#2435のダミー枠に#2541 紅梅(2)が追加されたということになる.この煽りで,MARGBOX #2396が紅梅(2)に直結するようになってしまったのだろう.それでは,紅梅のTYW枠はどこに接続しているのだろう?紅梅のTYW枠#2348:#2541 紅梅(2)+→#1328 紅梅(0)は紅梅(2)を直接参照している.これに対し,弘徽殿女御のTYW枠は「紅梅のTYW枠#2348」を参照している.明らかにこれは誤っていると考えられる.

TailShedding では処理を開始する前に対象スレッドのTYW枠を一旦外して安全な場所に移動した後,スレッドの伸縮を実施して,その後またスレッドの末尾につなぎ戻す操作を行っている.このとき,弘徽殿のTYW枠を共連れにしているような気がする.⇒TailShedding に入ってきたときの紅梅のTYW枠の位置がおかしい.最初から#2438 弘徽殿女御(3) と弘徽殿のTYW枠#2396の間に入っている.CutBranch(false)では下流系を連れて出るので,弘徽殿のTYW枠も一緒に移動してしまう.

最初の構図では確かにGetUpperNodeでは弘徽殿女御(3)をキャッチできるのだが,その後紅梅(2)が入って,紅梅のTYW枠がそれに連結されるため,弘徽殿女御(3)に到達できなくなってしまう.この不良はNAMEBOX #2347 紅梅(1)のTailShedding で起きている.入口では,

NAMEBOX::DumpLongTail NAMEBOX::TailShedding >>>>> #2347 紅梅(1)
   1  dum#=1 #2347 紅梅(1)
   2  dum#=1 #2434 紅梅(2)
   1  dum#=2 #2395 弘徽殿女御(2)
   2  dum#=2 #2438 弘徽殿女御(3)
NAMEBOX::DumpDummyChain NAMEBOX::TailShedding >>>>>
   1  thread #2347 紅梅(1) #2395 弘徽殿女御(2)
   2  #2435 [結婚枠]:[5] #2434 紅梅(2) #2438 弘徽殿女御(3)

のようになっているが,出口では

NAMEBOX::DumpLongTail NAMEBOX::TailShedding >>>>> #2347 紅梅(1)
   1  dum#=1 #2347 紅梅(1)
   1  dum#=2 #2395 弘徽殿女御(2)
   2  dum#=2 #2438 弘徽殿女御(3)
NAMEBOX::DumpDummyChain NAMEBOX::TailShedding >>>>
   1  thread #2347 紅梅(1) #2395 弘徽殿女御(2)
   2  #2435 [結婚枠]:[5] #2438 弘徽殿女御(3)

のように変化している.このとき,紅梅のTYW枠#2348のownerは弘徽殿女御(3)に変わり,Yリストもそのように変化している.これは明らかに誤りと思われる.

系列優先ノードの切り替えはEstablishMajorTribeChainの専管とする

源氏物語全系譜6.1.ZELの直系親族図を#125 髭黒で開いてNAMEBOX:MakeExtractBoxで停止する.oya->getmarglinkで空が返っている.getmarglinkは結婚枠の属する結婚リンクを求める関数だ.このチェックポイントは「全体図を#120 八宮の母女御でソートしてgetmargboxで停止するという障害の原因を突き止めるために暫定的に挿入したものだ.getmarglinkが空を返すというのはノーマルな動作だが,どのようなケースでそれが起きているのかを確認しておきたい.

抽出対象ノードはNAMEBOX #1593 蛍兵部卿宮(1)でMakeExtractBoxはNAMEBOX::CardShiftDirectの下位関数で,結婚枠内のノードとその下流系を抽出してその下にぶら下げる「長い尻尾」を構成するときに,その一段分の操作を実施する関数だ.この関数の主語は処理対象となる人名枠でoyaというのは,そのノードを格納している結婚枠であり,「親元結婚枠」である場合と,「抽出枠」である場合の2通りがある.今の場合はすでに1段目の処理は完了しているので,「抽出枠」だ.

この結婚枠はEXTRACTDUMMY|EXTRACTBOX|MOTHERLESSという属性を持っているので,「多重カード削減のためシフトされた抽出結婚枠」であることは間違いない.通常,結婚枠は結婚リンクに直接接続しているため,結婚リンクと一対一に対応しているが,抽出枠やTYW枠などは余分に追加された描画要素なので,変則的なトポロジーになっている.今の場合NAMEBOX #1839 蛍兵部卿宮(2)のNAMEsDUMMYスロットに接続している.蛍兵部卿宮(2)はEXTRACTDUMMYという属性を持っているので,「親元結婚枠」に残された代用ノードと思われる.

いずれにしても,親枠が結婚リンクを持たない場合があるということがわかった.しかし,これはかなり変則的な状態と考えられるので,このような状態を属性として保持しておくことにしよう.人名枠や結婚枠にはクリティカル属性というのがあるので,ここで設定することにする.HASNOMARGLINKという属性を新設し,NAMEBOXの場合にはsetmarglinkでセット/リセットするようにした.MARGBOXの場合は接続なので,MARGBOXのコンストラクタでチェックすることにする.

image

上図には「長い尻尾」が3本出現しているが,これは玉鬘と致仕太政大臣の親子関係から強制されるレイアウトだ.長い尻尾を使うことで玉鬘が多重となることを回避している.「全体図を#120 八宮の母女御でソートしてgetmargboxで停止」という元の問題に戻ってみよう.現行では重婚同類が存在するときにはShiftDirectAbsoluteを実行しないとしているので,停止しないようになっているが,ShiftDirectAbsoluteを実行するという論理に戻してみる.

NAMEBOX::getmargboxの出口検査で結婚枠不在で停止する.障害ノードはNAMEBOX #3416 桐壷院(2)で,クリティカル属性HASNOMARGLINKを持っている.NAMEBOX::TYW2ExtractBoxの主語は#1767 桐壷院(1)でTOOYOUNGWIFE属性を持ち,HASNOMARGLINKはオンになっている.桐壷院(1)の長い尻尾をDumpDummyChainで見ると,

1  thread        #3416 桐壷院(2) #3450 前春宮(1)
2  #3417 [結婚枠]:[2] #3424 桐壷院(3) #3452 前春宮(2)

のようなLDR束になっている.桐壷院(2)は親枠内のLDR,(3)はダミーノード,この下にTYW枠

MARGBOX #3425:#3424 桐壷院(3)+→#1767 桐壷院(1)

が配置されている.TYW2ExtractBoxではTYWチェーン先頭のLDRを固定ノードに切り替えてLDR束から外すだけで人名ノードの接続関係は変化していないはずだから,桐壷院(2)→ (3)→ (1)という順序も変化していないはずだ.このうち,HASNOMARGLINKは真ん中の(3)だけで,(2)と(1)は同じMARGLINK:#72 pnum=1 refnum=1 夫=CARDLINK:#424 @10一院[0] 妻=空 子=CARDLINK:#408 @2桐壷院[3]を参照している.さて,どこに問題があるのだろう?

NAMEBOX::resetghostbitsでは,「LDR束線分から離脱したときは,親参照を元の親の結婚枠に戻す@20210113」として,oya = getmargboxでoyaが空でなければ親参照を設定している.この処理は長い尻尾に属するすべてのノードを対象に実行されている.従って,中間にMARGLINKを持たないノードが出現するのは避けられないように思われるのだが… ⇒getmargboxではそれに備えた仕組みを備えている.つまり,対象ノードがダミーノードなどmarglinkを持たないオブジェクトの場合にはYリストを上昇して上位結婚枠を探すようになっている.

一般に系図木を階層的に見ると,人名枠と結婚枠がサンドイッチのように交互に層をなすようになっているから,本来なら必ず結婚枠を見つけることができなくてはならない.それができないのは,このノードがダミーノードとしてのEXTRACTDUMMY属性を失っているからだ.どこでそれがリセットされたかが問題だ.

このダミーチェーンは最初抽出枠チェーンとして生成された後,同じShiftDirectAbsoluteの中でExtractBox2TYWによってTYWに転換されている.このとき一度EXTRACTDUMMY属性をリセットされ,代わりにDUMMYBOX属性を与えられている.その後,SolveMargboxCollisionでTYW2ExtractBoxが実行され,resetghostbits(DUMMYBOX)でリセットされると同時に親参照付け替えのためにgetmargboxが呼び出されている.NAMEBOX::TYW2ExtractBoxではその直後にsetghostbits(EXTRACTDUMMY)が実行されるので,おそらく致命的な瑕疵にはなっていないのではないかと思われる.問題は解決した.桐壷院は結局,抽出枠で収まっているようだ.

image

重婚同類のときShiftDirectAbsoluteを実行することによる弊害というのがまだ残っている可能性もあるが,現在の構成で進めることにしよう.

同上サンプルの全体図を#191 左大臣(真木柱)でソートしてTRIBEBOX::SetMinorTribeで停止した.優先ノードが消去された仮ノードのとき,優先仮ノードの実ノードと系列の参照する実ノードが一致しないというエラーだ.障害が起きているのはTRIBEBOX #1569 先祖=#1258 一院(0)で,優先仮ノードはNAMEBOX #3476 冷泉院(5),実ノードはNAMEBOX #1371 左大臣の女御(0)だ.冷泉院(5)はLDRで関係は養子,左大臣の女御(0)は実子でいずれも本人ノードだ.BTW関係は成立していない.接続種別は婚姻関係になっている.

最終出力では左大臣の女御はBTWのRIGHTHUSBAND(右手結婚枠本人仮ノード)になっている.左大臣の女御は基準ノード左大臣 (真木柱)の子どもで,左大臣の女御+冷泉院には子どもがいないため,始系列にはこの2つのノードしか含まれない.

image

基準ノードの子どもを仮ノード消去するというのはよくないが,左大臣の女御+冷泉院の結婚が始系列で展開されているとすれば,そもそも仮ノード消去するようなことにはならないはずなのだが… まして,このノードがLDRというのは考えられない.どういう操作をするとそういうことになるのか?⇒いや,勘違いしていた.一院系列の冷泉院から左大臣系列への参照なのだから,そのようなことはあり得るかもしれない…

冷泉院(5)がLDR参照しているのは,同じ一院系列内の冷泉院(3)だ.このようなことはあり得るだろうか?冷泉院の仮ノードは5つあり,可視のものだけでも3個ある.ただし,うち一つはLDRなのでリアルに可視と言えば2つになる.これは避けられないだろう.最終的には一院系列は優先=#1766 冷泉院(3)→#1371 左大臣の女御(0)で収まっている.これはBTW左接続関係だ.「系列実ノードが一致しない」というエラーはDoublyBlessedOne→ ChangeRealRefferenceの過程で起きている.

この関数は,右手配偶者が実ノード参照されているとき,「右手配偶者が実ノードのときは左手本人に付け替える」ために実施されるもので,安定した状態とは言えない状況なので,OnEstablishMajorTribeChainの場合を除外しているように,除外してよいのではないかと思う.BTW処理中はOnDoublyBlessedOneが立っているので,これを見ることにしよう.⇒このエラーは回避できたが,別のエラーが残っている.

SetMinorTribe→ DoesMajorTribePathExistでhasPhysicalConnectionが呼び出され,(!realnode->getghostbits(REALGHOST))で停止するというエラーだ.⇒「ChangeRealRefferenceで系列優先ノードの切り替えは実施しない@20210119」ということにした.系列優先ノードの切り替えはEstablishMajorTribeChainの専管事項とし,BTWやTribeGhostNameは系列優先ノードの切り替えには関わらないというのがわかり易いと思う.

▲同上サンプルの直系親族図を#57 女三宮でソートして,NAMEBOX:TailSheddingで停止した.TRIBELIST::ShiftDirectAbsoluteで起きている.重婚同類が存在するので,オリジナル版ではパスされていたコードだ.ShiftDirectAbsoluteは重婚同類検定で決定した絶対世代番号に基づく垂直再配置なので,多重が入ってくると混乱が生じることは明らかだが,多重が存在する場合には重婚同類検定の結果がまったく使えないというのもおもしろくない話だ.

この辺りはもう少し改善の余地があると思われるので,追求してみたい.そのためには,重婚同類検定(以下多重検定)でどんなことをやっているのかを調べてみなくてはならない.多重検定ではグラフを3つも使っている.どんな用途で使っているのかをまず調べてみよう.

エラーを無視して続行で描画まで進むことはできる.多少不自然なところはあるが,何とかこなしているようだ.

image

重婚同類が3で多重が10発生している.うち1件は基準ノード自身の多重だ.系列は4系列でほとんどすべての領域を占める始系列の一院系列と,それに埋め込まれた二条太政大臣系列,※3系列,先帝系列からなる.後の3つはいずれも構成メンバー3~4の小さな系列だ.障害が起きているのはNAMEBOX #1927 光源氏(2)で,仮ノードと実ノードの世代差が-1になっている.

ロコモティブシンドロームに追い抜かれる?

昨日の続信

kamui さま

>早速の返信ありがとうございました。

「正式版はいつ出るのか?」という疑問をお持ちの方が多数おられますので,不躾ながら返信を「公開」させて頂きました.m(__)m

>是非、今年度中のリリースをお願いします。

ご寛恕いただきありがとうございます.時間が速いので…ロコモティブシンドロームに追い抜かれなければよいのですが…

>リリースしていただければ新しいパソコンも買えます。

最速のパソコンというのを使ってみたいですね!(わたしが使っているのはキャンペーンの特価品ばかりです)最近パッケージ全体をリビルドすることが多いので,痛感しています.これまでは時間・空間コストの節約(ケチること)を最大の課題としてきましたが(Windows 96で走るゼルコバの木クラシックなどというのはその最たるもの),そんなことを言っている場合ではないような気もしています.(アメリカ人の太っ腹な資源浪費に負けてしまいます)ゼルコバの木を動かすには超速マシーンが必要という時代が来るかもしれません…

>「システムの完全なグラスボックス化にあります」 これいいですね

スリムアップというのは一種の断捨離のようなもので,システム全体のクリーンアップ(大掃除)を目指しています.AIが人間の知能を超える「シンギュラリティ」の日が近いということが盛んに言われていますが,その前にニューロネットのブラックボックスと(我々の)グラスボックスが融合する日が来なくてはならないだろうと思っています(笑).

A.B.

源氏物語全系譜6.1.ZELの全体図を#81 弘徽殿女御で開いて,TRIBEBOX::hasPhysicalConnectionで停止する.系列優先仮ノードの属性がREALGHOSTのとき,実ノードが可視という理由だ.⇒おかしい.停止しなくなってしまった.TRIBEBOX::hasPhysicalConnectionのラッパ関数HasPhysicalConnectionを作っただけで,特に有意な修正は行っていないつもりなのだが…

障害が起きているのはTRIBEBOX #1569 先祖=#1258 一院(0)[2]で優先仮ノードはNAMEBOX #1255 秋好中宮(0),優先実ノードは#1766 冷泉院(1),主系列TRIBEBOX #1567 先祖=#1375 摂政太政大臣(0)[1] 優先=#1329 弘徽殿女御(0) 始系列にBTW右接続関係で接続している.障害はSetMinorTribe→ DoesMajorTribePathExist→ hasPhysicalConnectionで検出されている.

秋吉中宮(0)はREALGHOSTだが,同時に両手に花の右手結婚枠本人仮ノードでもある.こういうことは起こり得ることだ.⇒今度は動作するようになった… ⇒「実ノードが可視」のチェックは「実ノードが仮ノードを参照している場合」に限定するようにした.

同上サンプルを#98 夕顔でソートしてhasPhysicalConnectionで停止した.仮ノードがBTWRIGHTSPOUSEのとき,実ノードがDOUBLYBLESSED属性を持たないというエラー.障害は#1569 一院系列,優先仮ノードは#1762 冷泉院(1),優先実ノードは#1257 冷泉院(0).冷泉院(1)はBTWRIGHTSPOUSE(両手に花の右手結婚枠配偶者仮ノード),冷泉院(0)は三位中将系列でREALGHOSTだが,BTW属性は持っていない.系列接続種別はBTW右接続関係になっているが,その相手方ではない.しかし,どこかで最終解決を得ている模様だ.

出力では冷泉院(0)と(1)がBTWRIGHTSPOUSE,(2)がDOUBLYBLESSEDで収まっている.にも関わらず,「実仮ノード本人で系列種別が」というエラーメッセージが継続して出ている.これは冷泉院(2)が被参照実ノード属性を持っているためだ.この参照は冷泉院(3)→(2)の参照で,冷泉院(3)はLDRだ.系列種別がDecideTribeTypeと一致している場合は警告を表示しないようにした.

同上サンプルを#118 伊予介でソートしてhasPhysicalConnectionで停止した.障害系列は#1573 一院系列で優先仮ノードは#1763 光源氏(2),実ノードは#1760 空蝉(1).どちらもDOUBLYBLESSED|RIGHTHUSBANDの属性を持っている.系列接続種別はBTW左接続関係になっているが,hasPhysicalConnectionはPRIME_BTWRIGHTを求めている.しかし,DecideTribeTypeはPRIME_BTWLEFTを返している.なぜか?この2つのノードは相反する属性を持っている.なぜこういう状態になっているのか調べる必要がある.

少なくとも空蝉はDOUBLYBLESSEDとRIGHTHUSBANDの両側になり得る.伊予介との関係と光源氏との関係があるからだ.光源氏はDOUBLYBLESSEDだけのように見えるがどこかで解消しているのかもしれない.いずれにせよ,優先仮ノードがDOUBLYBLESSEDとRIGHTHUSBANDの両方を持っている場合,接続種別をどちらか一方に決めつけることはできない.どのように判定すればよいのか?DecideTribeTypeがどのように判定しているのかを見てみよう.

DecideTribeTypeでは実際に仮ノードの結婚チェーンを検査して一致するものがあればPRIME_BTWRIGHTを返しているが,そうでなければPRIME_BTWLEFTを同様の方法で検査している.DecideTribeTypeの検査は厳密で信頼できると考えられるので,むしろこの結果と一致しているか否かで判断するのがよいと思われる.

同上サンプルの直系血族図を#7 秋好中宮でソートして,TRIBEBOX:checkTribeVerticalPositionで停止した.TribeRelocationの出口近く,FOLDINGCHANNELSフェーズでFoldingChannelsを実行中のエラーだ.(PHASE > AFTERMARGSAMEGENE && !NOMULTICARD && senzo->IsValidNameBox() && !sameGeneCycles && !TRIBES && senzo->getFloor() != ancestor->getnodegene()) という条件で停止している.多重が存在しない状態で,先祖ノードの物理世代番号と絶対世代番号が一致しないというエラーだ.⇒エラーを無視して描画は可能.

image

この図面には一院系列,大臣(葵)系列,※3系列の3つの系列が含まれている.障害が起きているのはTRIBEBOX #1569 先祖=#1367 大臣(葵)(0)[2] 優先=#1577 六条御息所(2)→#1575 六条御息所(1)だ.「多重はない」としながら,六条御息所で多重になっている.絶対世代番号というのは,重婚同類検定で決定される数値だが,おそらくこのときには大臣(葵)の直下の六条御息所を光源氏の隣のカードと同世代位置に配置するという設計だったのに違いない.それが実現されていないのは,明らかにShiftDirectAbsoluteとAdjustTribeGenerationを(暫定的に)止めているための帰結だ.これを復活させて下図を得た.

image

産湯を捨てるつもりで赤子を流してしまうところだった(汗).

同上サンプルの全体図を#120 八宮の母女御でソートしてNAMEBOX:getmargboxで停止した.TribeRelocation→ MakePairListClean→ CompleteTribeBoxを実行しているところだ.(getY() && PHASE > GODOWNSTREAM && !disposing && getrelation() != REL_ROOTS && !hidden())という理由で停止している.この行には「結婚枠不在」というコメントが付いている.

getmargboxは人名枠の属する結婚枠を返す呼び出し頻度の高い汎用ルーチンだ.障害ノードは#3424 桐壷院(3)で一院系列に属する.関係は実子だが,TYW婚への参照を持っている.この参照リンクには「LDRチェーン末尾に接続するTYW結婚枠を上位人名枠から参照」というコメントが付いている.障害が起きているシチュエーションは,SolveMargboxCollisionで結婚枠の衝突が検出され,MARGBOX:TYW2ExtractBoxで関係するTYW婚を抽出枠に転換する処理が実施されているところだ.このエラーはかなり難しい.

このノードはMakeExtractBoxで生成されたときから結婚リンクを持っていない.ShiftDirectAbsoluteの中で生成されているので,この部分のロジックをオリジナルの重婚同類が存在するときには実行しないという論理に戻しておくことにする.この部分はあとで見直すことにしたい.

▲同上サンプルの直系親族図を#125 髭黒で開いて,NAMEBOX:MakeExtractBoxで停止した.oya->getmarglinkで空が返っている.これは上記の問題の原因とも言える箇所で,この値が空になっていると,上記のようなエラーが生じる可能性がある.#144 真木柱でも起きる.#125 髭黒の場合を見てみよう.⇒重婚同類は存在しないため,ShiftDirectAbsoluteが実行され,その中でエラーが発生している.

NAMEBOX::MakeExtractBoxでは,「対象ノードを親枠から一段シフトする」処理を実行している.MakeExtractBoxはCardShiftDirectから呼び出されてカードの一段シフトを実行している.CardShiftDirectは「結婚枠からこのノードとその下流系を抽出して結婚枠に格納し,このノードの位置にはEXTRACTDUMMYダミーノードを置く.抽出された部分結婚枠はダミーノードの下に配置する.抽出部分結婚枠はダミーノードのNAMEsDUMMYに接続する.」を実行している.今の場合はシフト量は2段で,ループの2回目でエラーが発生している.MARGBOX:getmarglinkには以下のような説明が付いている.

「LDR結婚枠の親ノードは結婚枠なのでgetmarglinkでは空が返る.抽出枠/TYW枠の親ノードは人名リンクなので空が返る@2018-01-23 ZTYW枠の結婚リンクを求める手続きを追加した@20180110」