縦系図と横系図の切り替えをサポートする

▲YGroup32.zelを開こうとしたら,MakePairListCleanで無限ループしてしまった.最初に以下のエラーが発生している.SUPPORTSIMPLEGRAPHをオンにしたためだろうか?

image

確かにそのようだ.オフにしたら描画できた.親子連結線の不良に関係があるかどうか見るためにオンにしたのだが,無関係のようだ.このオプションに関しては後日調べることにして戻しておこう.⇒YGroup.zelで不用な親子連結線が表示されている.これは少し気になるので,見ておきたい.画面右下の連結線はどこにもつながっていない.

image

この線はYGroupから出ているので,YGroupの子どもと考えられるが,hiddenとcheckcidの間には,printnamaeしかない.printnamaeは一世代下の箱に入っている.YGroupの子どもは13人だ.結婚点の位置もおかしい.⇒縦系図/横系図は,DrawObject.cppのbool reverseAxisで切り替えできる.結婚点などはその切替を行っていないのではないか?本来論理座標は一つであり,描画する側で対処する必要がある.reverseAxisはDrawObject.cppの中でしか参照されていない.

座標軸の変換関数として,reversePoint, reverseRectがある._CDCというCDCの派生クラスにはこれらを使った描画関数のセットがある._CDCを経由して描画していれば,間違いないはずだ.他の描画関数はほぼ対応済みと見られるが,Polygonは残ったままとなっていたものと思われる.⇒対処した.これで座標軸の転換は完結したものと見られる.

上記の不明な連結線の出所がわかった.

image

NAMEBOX::DrawLongTailでCheckYrouteのLDR垂線として描画している.しかし,CheckYrouteのLDRはYGroupの子どもとしてすでに描画されており,CheckYroute自身は実体を持っている.つまり,余分なノード対を持っているように思われる.何か操作しているときに誤って残してしまったのではないだろうか?このノード対は,CheckYroute(2)→ (1)を連結するものだ.本来の(1)→(2)はノーマルに描画されている.このようなノード対が発生していること自体問題だ.段数が2となっている.どうも,LongTailを作り損なっているのではないか?

NAMEBOX::DumpLongTail NAMEBOX::DrawLongTail > #1157 CheckYroute(2)
   1  dum#=1 #1157 CheckYroute(2)
   1  dum#=2 #1169 YRight(2)
   2  dum#=2 #1905 YRight(3)
NAMEBOX::DumpDummyChain NAMEBOX::DrawLongTail
   1  thread        #1157 CheckYroute(2) #1169 YRight(2)
   2  #1913 [結婚枠]:[2] #1905 YRight(3)

長いしっぽの構成を忘れてしまった.確か複合的なものであったような気はするが…YRightはYgroupの子どもであり,CheckYrouteとはつながらないような気がするのだが…⇒なぜ,複合構造になるかというと,LDRは複数のLDRが束ねられる場合があるからだ.確かにYRightはYGroupの子ノードであり,CheckYrouteと共通の親を持っているから,LDR束だ.しかし,YRightは親を2人持っていて,それがCheckYrouteだ.どうもかなりややこしい話になってきた.

出力ではYRightはYgroupの子ども枠の中にLDRはとして入っているように見える.結局,この線分は下図のように引かれなくてはならなかったものと思われる.

image

従って,描画していることではなく,描画している位置が間違っている,つまり,一世代下がり過ぎということになるのだろう.⇒できた!

image

かなりノーマルなモードに近づいた.バックアップを取っておこう.⇒TYW2ExtractBoxでは問題なく動作することが確認できたが,RestoreYoungWifeとどこが違うのかをチェックしておく必要がある.このために,関数の参照関係を洗い出そうとしたのだが,関数木の原本が見つからなくなってしまった.いや,その前にYGroup32.zelで出ていた部分図の障害を見ておこう.というか,この図面は相当乱れている.

image

いや,今度はノーマルに開けた.インストール版でテストしていた.修正の入った版では解決している.

image

YGroup32.zelで「人名枠線を表示する」をオンにしようとして,エラーになった.ResetTooYoungWifeだ.

image

(topology->TooYoungList->len() != ztywcount) が起きている.

ztywcountは-1で,TooYoungListのデータカウントはゼロだ.どこかで引き過ぎているのだろう.ztywcountはZTYWのカウントでTYWのカウントではない.この辺りで食い違いが生じているのでは?明らかに行き違いがある.TooYoungListに格納されているのはZTYWだけではない.それにしてもおかしいことはおかしいのだが…ztywcountをtywcountに統合するのではなく,別途tywcountをカウントすべきだろう.同様に,SymmetryCountも別途カウントすべきだ.つまり,システムの内部パラメータはつねに,2つの方法でカウントされなくてはならない.言い換えると複式でカウントされなくてはならないというのが原則だ.この考え方は徹底されなくてはならない.

  1. 系列枠は個別にTooYoungCount, ZTYWCount, SymmetryCountを管理している これに対応して大域的にtywcount, ztywcount, symcountを管理するものとする
  2. これらのカウントはそれぞれの属性のセット・リセットを行うsetghostbits, resetghostbitsで同期を取るものとする
  3. TouYoungList, SymmetryListへの追加,削除のタイミングで上記カウンタとの一致を確認する
  4. 系列枠のカウンタを更新した場合にもそれらの合計と一致していることを確認する
  5. これらの属性はすべて結婚枠の属性であるとする
  6. TOPOLOGY::CountTooYoungWifeという集計関数もある

TopologicalSortの冒頭でTOPOLOGY::ResetExperimentが実行されている.このとき,3つのカウンタがリセットされているが,オブジェクトは温存されているためカウント不一致が発生する.⇒SymmetryList,TooYoungListの廃棄とtywcount,ztywcount,symcountのリセットはClearTableより後に実行することにした.ResetExperimentもそのタイミングでよいのではないだろうか?ただし,genelist, tribeListの初期化はClearTableに先行させる必要がある.

YGroup32.zelでは多重カードが3件出ている.これは削減できないのだろうか?あるいは,どこかで機能劣化してしまった可能性もある.

image

一番左のYGroup(♀)は祖父のYGroup(♂)の配偶者なので,多重となるのは避けられない.真ん中のCutBranch(♂)は娘のCutBranch(♀)の配偶者なのでやはり不可避だ.CheckYroute(♂)は親を3組持っているが,そのうちの一人は兄弟のCutBranch(♂)なのでやはり,避けられないということのようだ.これは,再帰ないし再入呼び出しを表現するための苦肉の策として取られた手段ではなかったろうか?

ゼルコバの木では血統循環を禁止しているため,婚姻関係を使って擬似的な血統循環を成立させていたのだと思う.⇒関数木では再帰的な呼び出しを,同名の子ノードを作りそれを親ノード(呼び出し先)と配偶させることで擬似的に表現している.再帰呼び出しは必ず血統循環を発生してしまうので,ゼルコバの木では禁止されているが,多重カードになることを許容するのなら,血統循環を許すという設計も(将来的には)考えられなくもない…

関数とその所属するクラスとの関係もある種の木を構成すると考えられるが,関数木ではクラスを部分図で代替している.ゼルコバの木では最大32個までの部分図しか登録できないが,ゼルコバの木の実装ではおおむねそのくらいの数のクラスしか扱わないので,ほぼカバーできるのではないかと思う.⇒部分図自体を一つのオブジェクトとしてその包含関係を明示できれば,部分図木のようなものを構成することも可能となるが,現在の実装では部分図は単純な集合であり,必ずしも順序関係を定義することはできないので,多分実装は難しいと思う.(関係グラフのようなものは構成できるかもしれないが…)

TYWに関係する処理の見直しを進めている

TYWに関係する処理の見直しを進めているところ.基本的には,「moveがゼロのときはZTYWとしない」という方針だが,障害が発生する原因は突き止めておかなくてはならない.また,衝突回数上限を決めるMAXCRUSHCOUNTと処理終了するまでの時間が必ずしも比例するものではないという点に関しても調べる必要がある.現行では,ZTYWに制限を掛けていないため,衝突回数オーバーですべてのTYW婚を解除するAbortAllFloatingBoxが発動しているが,にも関わらず障害が止まらないという問題がある.

このとき,①Yリスト上位ノード不在,②TYW婚の吸収合併成功,③NAMEBOX:CheckUnerasable 結婚枠不在のような事象が大量発生している.従来論理ではAbortAllFloatingBox→ StopTooYoungWife→ TYW2ExtractBoxでTYW→抽出枠転換していたが,現行では暫定的にRestoreYoungWifeを使っている.この理由は,TYW2ExtractBoxでは抽出枠が残存してしまうが,RestoreYoungWifeなら完全に元の状態に復元できると考えたためであるが,どうもどこかに不備があるような感じだ.⇒TYW2ExtractBoxを復活させれば正常動作可能だ.

TYW2ExtractBoxではZTYWの場合は,RestoreErasedYoungWifeが実行される.RestoreErasedYoungWifeではRestoreErasedYoungWifeで消去された仮ノードを取り出して,RetrieveGhostでそれを復活させている.従って,この処理にはぬかりはないと言える.逆にRestoreYoungWifeではそれが不十分な動作になっているように思われる.この辺りは,一度整理してドキュメント化が必要だ.呼び出し関係だけでもチェックしたいのだが,その手始めとして「関数木.zel」を復活させるというのは悪いアイディアではない.どこかにあるはずだ.

デスクトップ 2020-02-11\テストサンプル\関数木グラフというフォルダがあった.ただ,一部関数のコレクションばかりで,全体木のようなものが見当たらない.フォルダ名は2020となっているが,中身は2017~18に作られているようだ.どこかにもっと大きなサンプルがあるはずなのだが…別の場所にもいくつか関数木という名前のファイルはあるが,NONファイルに変換されてしまっていて,名前が短縮されているものしか見当たらない.2018-02-04の記事には「血統循環関係の関数参照木を作って不用関数を整理」とある.

デスクトップ 2020-02-11\テストサンプル\関数木グラフ\AccidentalCollisin.zelを開こうとしてエラーになった.

image

エラーを無視して描画は可能.⇒開発環境ではエラーにはならない.対処済みと思われる.

もっと大きな網羅的な関数木を作っていた記憶があるのだが,残っているのはどれも,特定の処理に関するコレクションばかりだ.もっとも大きなYGroup32.zelでも58点しか入っていない.

▲ YGroup32.zelで部分図 Bobject を選択して,(topology->TooYoungList->len() != ztywcount)で停止.図面もおかしい.カード数6と表示されているが,実際には7点あり,親子連結線もおかしい.⇒いや,カード数は6で正しい.多重カードが出ている.多重カードの解消機能が十分動作していないように見える.

ここでリリース版を起こしておこう.⇒インストールしたが,動作がおかしい.古い版が残ってしまっている.アンインストールして再インストールで正常動作するようになった.

moveがゼロのときはZTYWとしない

MakeZeroTYWで「moveがゼロのときはZTYWとしない@20230219」として,描画できるようになった.155件のTYWが成立している.ZTYWはゼロだ.TYWは世代差がある場合にのみ適用されるのではなかったろうか?だとしたら,TYW存在すること自体おかしいということになるのだが… ⇒こんな感じになっているようだ.

image

2601と5203は別々の箱に入っているので,兄弟枠内の間隔よりも離れている.このため,この2つの子ども枠を結婚点一致させようとすると衝突が発生してしまうという理由でTYWになっているのではないだろうか?ただし,これはTYWではなくZTYWではないかという気がするのだが…⇒TYWを止めるとどうなるかを見てみよう.⇒確かに,ルール通りの動作になっている.

image

従って,動作的には問題ないと言えるのだが…TYWのリセットが頻発しているのはなぜか?⇒結婚枠の衝突には許容上限があり,TYW枠がMAXCRUSHCOUNTを超えると抽出枠に落とされるようなルールになっている.たとえば,@4067のTYWはリセットされている.TYWがリセットされるとTooYoungListからも外されて,リセットされたノードがZTYWの場合はそこで処理が終了する.従って,生き延びたTYWはそのまま残存しているということにはなるが,ZTYWカウントがゼロというのはなぜか?

ZTYWは系列枠ごとに集計しているZTYWCountを合算したもので,CheckMultiCardsで集計している.この数が当てにならないのではないか?大域変数 ztywcount は人名枠属性を設定するところで直接カウントするべきだろう.ただし,この値はMARGBOXのIsZtywBoxを見ていることに注意.IsZtywBox では,(ERASEDYOUNGWIFE|TOOYOUNGWIFE) を見ているが,ここではERASEDYOUNGWIFEだけを見ることにする.

方式変更してZTYWが173と表示された.ただし,TYWが155で数が少ない.基本的にこのサンプルではTYW=ZTYWとならなくてはならないはずだ.どこでこの食い違いが生じているのだろう?MARGBOX:ResetTooYoungWifeでは,TOOYOUNGWIFEはリセットされているが,ERASEDYOUNGWIFEは放置されている.⇒対処した.これでTYWとZTYWのカウントが一致した.つまり,ZTYWが173個生成され,うち18個が解除されて155個残ったということにになる.

赤色表示しているはずのTYW本人ノードがまったく見えないのはなぜか?⇒おそらく,TYW本人ノードというのは不可視になっている方だろう.⇒TOOYOUNGWIFEを含めていなかった.⇒出てきた.

image

これで見ると,なぜTYWになるのかがよく分かる.これでよいのではないか?結婚枠衝突の上限はもっと小さくてもよいのではないだろうか?現行ではMAXCRUSHCOUNTはMAXCHANNELFAILUREにそろえてある.MAXCHANNELFAILUREは現在20だ.MAXCRUSHCOUNTはいろいろなループの上限として使われているが,直観的には10回くらいで十分であるような気もする.

MAXCRUSHCOUNT=10という設定ではTYWは129まで減少する.系統並び替えの所要時間は104秒.従来の設定に戻すと,時間はむしろ短縮して73秒になった.ほぼ無制限=100という設定では,処理時間が相当長引くほか,「不可視ノードの座標補正」というのが入って停止しなくなる.どうも,やはりある程度の歯止めがないと無理なようだ.MAXCRUSHCOUNT=30でも抜けて来なくなる.どうも,この値はかなりクリティカルだ.この場合もやはり,不可視ノードの座標補正というのが入る.「不可視ノードの座標補正」というのは,ほぼ検定に失敗したことを意味している.とりあえず,ここは現行のママとしておこう.

ここで,moveがゼロのときはZTYWとしない@20230219を復活させてみよう.⇒衝突検定でループカウントオーバーになる.事後の後処理中,NAMEBOX::RestoreYoungWifeで(!getghostbits(ELDERWIFE|ERASEDYOUNGWIFE))のエラーになる.これは論理的におかしいので,調べておこう.TooYoungListに記載されたノードで属性を持たないものが検出されたというエラーだ.RestoreYoungWifeは結婚枠と同期を取っているので,結婚枠と本人ノードに不一致が生じていることになる.つまり,本人ノードのELDERWIFE|ERASEDYOUNGWIFEをリセットするときに,結婚枠の属性が放置されたままになっている.

MARGBOX::ResetTooYoungWifeの他に,NAMEBOX:RestoreExtractBoxでもリセットしている.この中でもIsTooYoungWifeの場合は,ResetTooYoungWifeを呼び出しているのだが… 衝突回数オーバーなどでなにかイレギュラーな操作を行っているのではないだろうか?しかも,このケースではtywboxを削除までしているので,リストに残っている可能性はゼロだ.

ERASEDYOUNGWIFEはMARGBOX::ResetTooYoungWife,NAMEBOX:ExtractBox2LDRsub,NAMEBOX::ConvertZTYW2TYWでもリセットしている.ResetTooYoungWifeではTYW婚をリセットしているので,結婚枠もリセットしなくてはならない.⇒いや,それはやっている.ExtractBox2LDRsubでは抽出/TYW枠のチェーンをLDRの長い尻尾に転換している.この関数の中でもResetTooYoungWifeが実行されている.ConvertZTYW2TYWではTYWをZTYWに変換している.この場合はTYWはリセットされないので問題ないはずだ.

TRIBEBOX::StopTooYoungWifeの修正に誤りがある.仮修正@20230217だ.従来論理ではTYW2ExtractBoxでTYW→抽出枠転換していたものを,RestoreYoungWifeで廃棄するように仮修正しているが,呼び出しの主語が誤りだ.⇒修正して動作するようになったが,別のエラーが出てきた.①衝突検定ループカウントオーバー,②Yリスト上位ノード不在,③TYW婚の吸収合併成功,④NAMEBOX:CheckUnerasable 結婚枠不在,⑤多重カードが怒涛のような嵐となって吹き荒れている.それでも,エラーを無視して描画できた.

続きをやろうとしたら,初っ端からエラー

昨日の続きをやろうとしたら,初っ端からエラーに見舞われた.

▲サンプルAを開いて,RestoreYoungWifeのエラーで停止した.

image

昨日の修正で作り込んだエラーだ.バックアップに戻ろうとしたが,昨日の修正がまったく入っていない.昨日の修正はかなりボリュームがあるので,やり直しだけは勘弁してほしい.⇒TYWに関係する部分なので,「コラッツ特注版ではTYW婚不用@20211217」をオンにすれば戻ると思ったが,オンになっていた.どういうことだろう?かなりおかしい.⇒いや,違う.いま見ているのはバックアップの方だ.

「コラッツ特注版ではTYW婚不用@20211217」で描画できるようになった.タモリさんちの拡張家系図や渋沢一族なども(概ね)問題なく描画できている.そうなると,TYW婚というのは不用なのではないか?という気もしてくる.TYW婚の起源まで遡るのは難しいかもしれないが… どういう事情で導入することになったのか?その経緯が知りたい.

2021/01/21の記事に,弘徽殿女御がTYWになっているという記録がある.源氏には複雑な関係が多いので,その加減かもしれない.

▲部分図の選択解除という操作がないのは仕様的に問題だ.源氏6を開いた状態→新規部分図でエラーが発生した.また,このサンプルでは多重カードが11件発生している.特に光源氏と冷泉院は3重カードになっている.⇒TYWオフになっているためだろうか?⇒TYW婚をオンにしても多重は解消しない.ただし,源氏と冷泉院の3重カードというのは出なくなった.血族婚が存在する場合には多重は不可避だったような気がするので,11件のうちのいくつかはそれに該当するのではないかと思われるが…「異世代多重反復カウントオーバー」というのが出ている.

いずれにせよ,TYWという機能は必要と思われるので,コラッツサンプルでなぜ多量のTYWが発生するのか?また,TYWを停止しても収束できないのはなぜかを突き止めなくてはならない.

▲TRIBEBOX::StopTooYoungWifeの中で停止する.TooYoungListに記載されているノードでELDERWIFE|ERASEDYOUNGWIFE属性を持っていないものがある.

V.2.3.0.000 R.2023-02-17 をリリース

仮修正が少し残っているので,フィックスしておこう.

  1. BCMDTABLE::BCMDTABLEの仕様変更:呼び出し時の引数を追加 ⇒ 現状でフィックス
  2. TRIBEBOX::HorizontalSegment:セグメント数=1のとき,CheckMaximalSegmentで再検査→廃止 ⇒ フィックス
  3. TRIBEBOX::HorizontalSegment:出口でCheckMaximalSegmentの再検査→廃止 ⇒ 現状でフィックス
  4. TRIBEBOX::MinimumContactDistance:すでに検定完了している場合は停止するようになっていた→停止しない ⇒ FIXED
  5. TOPOLOGY::ClearTable:MARGBOXの初期化でdataCleanを使う ⇒ FIXED
  6. ClearTableでlookupを使う@20230214 ⇒ FIXED

COUPLING::TopologicalSortでEraseTreeViewを呼び出さないようにしたところ,系統並び替えを実行しようとして,ClearTable→ NAMEBOX::CLEARで停止した.TRIBELISTのgetstarttribeが空を返している.tribelistはすでに廃棄されているが,基準ノードは始系列の優先ノードなのでSelectBaseBoxで参照が掛かっている.⇒startが空のときは処理をパスするようにした.

これでEraseTreeViewを実行しなくても,処理を継続することができるようになった.ClearTableを止めるのは無理のようだ.現在,系統並び替えでは13.7秒掛かっている.うち,ClearTableの消費時間は407msだ.サンプルBではClearTabledeだけで20秒掛かっている.その後のステージ9:Decomposition,ステージ11:GODOWNSTREAMもかなり掛かる.⇒CheckMaximalSegmentの出口で行っていたCheckMaximalCompactionの再検査を止めてかなり高速化した.それでもサンプルBの系統並び替えでは253秒で4分以上掛かっている.とりあえず,今のところパフォーマンスについてはここまでとしておこう.

◎現在開発機にインストールされているのは,V2.2.2.008 REL 2022-04-26 でカードテーブル上限は8192だが,サンプルAを読み込んでもまったく表示できない.現行バージョンのリリース版を起こしてみよう.

リリース版をビルドしようとして,STOPマクロでエラーが出ている.Dump関数をCObject::Dumpと取り違えている.なぜだろう?CObjectはMFCライブラリの基底クラスなので,我々も使ってはいるが… ヘッダはafx.hだ.DrawingObject.hはいつも最後に読み込まれるので,それ自身は他のヘッダをまったく参照していない._WIN32_WINNT not defined. Defaulting to _WIN32_WINNT_MAXVER (see WinSDKVer.h)という警告が出ている.⇒DrawingObject.hではCDCの派生クラスを定義している.おそらく,この辺りでMFCのクラス定義が持ち込まれているのだろう.

意味がわかった._CDCはCDCの拡張クラスなのだから,その中から呼び出すとすれば,当然その系統のメンバー関数になる.ということは,当然_CDCの中からNODULEクラスの関数を呼び出すことはできないということだ.しかし,どういう訳かデバッグモードでは動作しているので切り分けておくことにしよう.QUICKDBでもエラーが出ている.コードは変化していないはずなのだが…⇒いや,PickRecordは今回新設した関数だ._STOPなら使える.

IsAinstanceOfで_mbscmpの代わりにstrcmpを使うようにした.

nodule::CheckSansyoCountでSTOP文野中で,Dump「静的でないメンバー関数の呼び出しが正しくありません」エラーになった.⇒CheckSansyoCountは確かに静的関数として宣言されている.⇒_STOPなら問題ないようだ.

SetupTitleBox.vb(3274,24): error BC30451: ‘CommandLine’ は宣言されていません。アクセスできない保護レベルになっています。⇒CommandLine As Stringは以前はWindows.vbでpublicとして定義されていたのだが,現在はMDIForm_Loadのローカル変数になっている.UnlockTitleではスプラッシュウィンドウをクローズするための判定に使っているが,UnlockTitleの中でスプラッシュウィンドウを操作するというのはおかしいので,止めておくことにしよう.

なかなかいい感じだ.リリースモードではサンプルAを5秒以内に開くことができる.サンプルBの系統並び替えが55秒だ.悪くないと思う.バージョン番号を更新しておこう.現在のバージョンは2.2.2008だが,2.3まで上げてよいのではないだろうか?⇒インストールに成功した.

パッケージにはテーマファイルは入っているが,サンプルがまったく入っていないような気がする.配布パッケージの添付ファイルを置くとしたらドキュメントフォルダがもっとも適していると思われるのだが… どこかの時点で外してしまったのだろう.⇒対処した.

▲タモリさんちの超複雑な家系(拡張版).zelをアイコンのダブルクリックで開こうとしてエラーになった.標準版のタモリんちは開けたが,横書きだ.

image

横書きと縦書きをオプションで切り替えられるようにする必要があるが,横書きでも違和感のないレイアウトになっている.ただし,描線にはあちこち問題がありそうだ.というか,現行では結婚線というのをまったく見ていないはず.ルーゴン・マッコール家系図でも同じエラーになった.

image

それに続いて,以下のようなエラーが出る.

image

軟体動物も開けたが,それ以外は全滅だ.

image

この原因として考えられるのは,記録データが取り込まれていないのではないか?という点だ.おそらくタモリんちと軟体動物には記録部への書き込みがまったくないのだろう.実際,コラッツで作ったサンプルは相当大きなものでも問題なく開けている.12146点のサンプルも問題なく開けた.ともかく,ここから再出発するしかない.

「ユーザの個人用データフォルダ」というのに格納するようにしたら,ドキュメントに入るようになった.バラバラで入れるのも何なので,「ゼルコバの木系図ファイル」というフォルダを作ってそこに入れるようにした.この問題は,まず,これで解決だ.

KAKEIZU::getcarddataでエラーが出ている.item[27]が空になっている.撮影日付だ.この部分は,QUICKDB::pickrecordを廃止して,PickRecordを新設しているが,論理は基本的に変わっていないはずなのだが…つまり,データが存在しない場合には空のままとしていたはずなのだが…実際の個別データはquickfile(“TAKE:8”)で取り出していたので,必ずポインタを渡すようになっていたのかもしれない.実行関数はquicktakeだ.以前は一件ごとにrespbuffというところを使って受け渡ししていたので,ポインタが空ということは発生する余地がなかったのだが…データがないときは空文字列を指すポインタを渡すというのでもよいが,それも煩わしい気がする.ポインタが空のときは無動作とするというのが早いのではないだろうか?文字列関数でエラーを出さないようにするということも考えられるが…

▲天皇家系図でエラーが出た.

image

MergeUpperSegmentでエラーが出ている.(head == mbox && mbox->realsons && !mbox->getCritical(FATALSTARTEND|NOGOODSONEXIST) && mbox->NextSibling()) のような条件でmakeGoodSonを呼び出してgoodsonが特定された場合に停止するようになっている.停止しないようにして描画できた.那須記も描画できたが,かなりおかしなことになっている.

image

添付ファイルは一通り開けることだけは確認できたが,かなり悲惨なことになっている.一番惨めなのは源氏かもしれない.一見して相当緩くなってしまっている.これを締め直すのはかなり大変そうだ.もっとひどいのは,Ancestry.zelだ.これはほぼ壊滅と言ってよい.とりあえず,読み込むことができたというだけでもよしとするしかない.

「結婚連結線をサポートしない@20220228」と「MakePairListCleanを一時止める@20220215」を止めてかなりまともな図面が出てきた.連結線がところどころ切れているのと,ゴミ粒のようなものが散らばっているのを除くと,ほぼまともな図面に戻ったと言ってよい.

▲天皇家でかなりひどいスプリットが出ている.

image

▲53直系血族図.ZELを開いて,TRIBEBOX::SolveMargboxCollisionで衝突検定ループカウントオーバーが起きている.986個のTYW婚を廃棄したとしているが,衝突は収まらない.「コラッツ特注版ではTYW婚不用@20211217」を復活させたら収まった.

どうも失敗してしまったようだ

どうも失敗してしまったようだ.ClearTableには2種類ある.一つはBASETABLE用で,もう一つがいま問題のTOPOLOGY用のものだが,取り違えていた.間違え易いのでリネームしておくことにする.BASETABLEの方をResetTable変更した.

NODULE クラス共通定義の整理

計算効率は作業効率に大きく影響するので,受忍限度内にファイルが開けるようになるまで処理速度を向上させるという目標でパフォーマンスの改善に取り組んでいるところだが,目下のターゲットとなっているClearTableでは,処理速度を上げることよりも,処理の透明性を確立することを主目的とする方向に転じることにする.処理の透明性は最重要課題であり,それが崩壊してカオス状態に陥る場所がCLEARTABLEフェーズであると考えられるので,この場所を完全に抑え込むことができれば,完全にクリーンなシステムという最終目標はほぼ達成されたと見てもよいはずだ.これはNODULEシステムの純度を上げ,完全に閉じたシステムとして独り立ちさせるためには不可欠の工程と考えられる.

このためには,COMMONHEADERCOMMON/SHORT/longbrPARTをきっちり整理する必要がある.手始めに共通ヘッダ定義の部分をテーブルの形にまとめてみた.NODULEクラスの仮想関数の一覧もテーブル形式にまとめた.これで大分見通しがよくなったのではないかと思う.Open Live Writer ではテーブルの編集には十分ではないので,ログインしてWordPressのエディタを使って編集するようにした.テーブルサイズがかなり大きいので持て余し気味だが,まぁ,なんとかなりそうだ.

CLEARTABLEは基本的には描画オブジェクトを初期化するというフェーズであり,基本データであるリンクデータには手を触れないというのが原則であると考えられるが,現行では系統並び替えに入る前に一度EraseTreeViewで描画オブジェクトを一度完全にパージしているはずなので,あるいは,すでにまるごと不用になっている可能性もあるのではないか?それとは逆に,画面要素を可能な限りキープして,更新された部分だけを再計算するという考え方もある.そのようなことが可能であるのか,可能でないのかも検討課題とする必要があるのではないか?

ClearTableを完全に停止してみたが,まったく問題なく動作している.ただし,前回は系統並び替えで14秒だったのが17秒と逆に増加している.これはどういうことだろう?いや,今回は復活させても17.75秒なのでやはりいくらかは早くなっている.時間が遅くなったのは,別の理由と思われる.この逆にEraseTreeViewを実行しないとどういうことになるのかを見てみよう.⇒まったく問題なく開けた.系統並び替えの時間は若干短縮して13.17秒になった.

ファイルを初期オープンしようとするとき,EraseFamilyTreeが重複して掛かってくる.⇒アプリ側はファイルをオープンする前にデータベースのクローズ要求を出してくる.DLL側ではファイルオープン処理の中でデータベースクローズを実行しているため.特に実害はないので,ママとしておこう.

ClearTableとEraseTreeViewの両方を止めてもまったく問題なく動作している.つまり,これらの処理はすでに不用になったと考えられる.これで,不連続点ないし不連続面が完全に払拭されたということになる.もはや最終形態に近いと言ってもよいのではないだろうか?これらの処理が入っている理由の一つとして,デバッグ時の再現性の問題がある.つまり,初期状態が完全に一致していないと,再現が困難ないし不能になる場合があり得るため,厳密に同じ状態から処理を開始する必要があるという理由だ.しかし,仮にそのような問題があり得るとしても,それはそれでまた別の方法で対処可能と考えられるので,ここでは,強引に壁を突破して前に進むことにしよう.

再現性の問題を除けば,(動作不良が発生しない限り)完全に連続した環境をキープすることに関しては何の問題もない.むしろ,そうあるべきだと言えるだろう.最後に残る問題は,系統並び替えと部分的な再描画が両立するものかどうか?という点に絞られてくるのではないか?トポロジーが変化しない限りはYリストも不変であり,座標値の再計算だけを行えばよいはずだが,トポロジーが変化したときには当然Yリストにも反映されなくてはならない.それを局所的に対処することが可能であるのなら,部分的な再描画も可能になる可能性が出てくる.

再描画の問題は局所計算の問題にも関わりがある.つまり,巨大図面の一部のみを計算して,必要になったときのみ,必要になった部分のみを再計算するという考え方だ.つまり,グーグルマップのようなことができるようになるためには,どうすればよいのか?という話なのだが…

NODULE クラス共通定義

NODULE クラス共通定義

COMMONHEADERBASIC(BASECLASS, CLASSNAME, CID, VSLOTS)
No. scope return value function name parameters process macro
1 virtual char* classname void #CLASSNAMEの取得 基本
2 static unsigned short classid void CIDの取得 基本
3 int getclasssize void sizeof(CLASSNAME)の取得 基本
4 unsigned short getcid void cidの取得 基本
5 nodule ** VSLOT void noduleポインタのアドレス→(BPTR)this + sizeof(BASECLASS)) 基本
6 nodule *& _SLOT int edan int bases=BASECLASS::_vslotsize();
if (edan <= bases)
return BASECLASS:_SLOT(edan);
else {
nodule **vslot=CLASSNAME::VSLOT();
if (vslot) return vslot[edan – bases – 1];
else return null; }
基本
7 virtual nodule *& SLOT int edan if (edan == EXTRASLOT) return extraslot();
else if (edan == REFERIST) return referlist();
int xtotal=vslotsize();
if (edan > xtotal) {
if (IsNotVital())
return NODULE::SLOT(edan);
else return null; }
return CLASSNAME:_SLOT(edan);
基本
8 int _slotsize void VSLOTS 基本
9 int _vslotsize void CLASSNAME::_slotsize() + BASECLASS::_vslotsize() 基本
10 int vslotsize void CLASSNAME::_vslotsize() 基本
11 void * dataptr void (BPTR)this + sizeof(BASECLASS) + VSLOTS * sizeof(long) 基本
12 static long datalen void sizeof(CLASSNAME) – sizeof(BASECLASS) – VSLOTS * sizeof(long) 基本
13 void dataclean void memset(dataptr(), 0, datalen()) 基本
14 void * dataPtr void dataptr() 基本
15 static long dataLen void datalen() 基本
16 void dataClean void memset(dataPtr(), 0, dataLen()) 基本
17 void CleanSlot bool refclear=false int start=BASECLASS::_vslotsize() + 1;
int slotsu=CLASSNAME::_slotsize();
for (int i=start; slotsu–; i++)
clearSlot(i, refclear);
基本
18 void Clean void doClean();
int slotsu=CLASSNAME::vslotsize() + 1;
for (int i=0; slotsu–; i++) {
nodule *snod=SLOT(i);
if (snod && snod->getpnode() == this  && snod->getpnum() == i && snod-
>IsVital() && !snod->IsEmbedded())    snod->Clean(); }
基本
19 void setpid void vslots()=CLASSNAME::_vslotsize();
if (!pclassid[CID]) {
pclassid[CID]=(unsigned short)
BASECLASS::classid();
vsfunctable[CID] =
(VSLOTFUNC)&CLASSNAME::_SLOT; }
基本
COMMONHEADERPART(BASECLASS, CLASSNAME, CID, VSLOTS)
20 int CleanSansyo nodule *object 固有
21 virtual void Dispose void 固有
22 virtual char * dump char *buff 固有

上記2つの共通マクロの他にCOMMONHEADERSHORTが定義されている.COMMONHEADERSHORTにはCOMMONHEADERBASICが含まれる.COMMONHEADERPARTは,COMMONHEADERSHORTに上記3関数の宣言を追加したものである.関数本体は別途記述されなくてはならない.

#define COMMONHEADERSHORT(BASECLASS, CLASSNAME, CID, VSLOTS) \
COMMONHEADERBASIC(BASECLASS, CLASSNAME, CID, VSLOTS) \
void *operator new (size_t size, nodule *moto=NULL, int eda=0, long ds=0, int addslot=0, short cid=CID, char *namstr=#CLASSNAME){ \
return nodule::operator new (size, moto, eda, ds, addslot, cid, namstr); } \
void operator delete(void *nod, nodule *moto, int eda, long ds, int addslot, short cid, char *namstr){ \
nodule::operator delete(nod, moto, eda, ds, addslot, cid, namstr); } \
void operator delete(void *nod){ nodule::operator delete(nod); }

#define COMMONHEADERSHORT(BASECLASS, CLASSNAME, CID, VSLOTS) \
COMMONHEADERBASIC(BASECLASS, CLASSNAME, CID, VSLOTS) \
void *operator new (size_t size, nodule *moto=NULL, int eda=0, long ds=0, int addslot=0, short cid=CID, char *namstr=#CLASSNAME){ \
return nodule::operator new (size, moto, eda, ds, addslot, cid, namstr); } \
void operator delete(void *nod, nodule *moto, int eda, long ds, int addslot, short cid, char *namstr){ \
nodule::operator delete(nod, moto, eda, ds, addslot, cid, namstr); } \
void operator delete(void *nod){ nodule::operator delete(nod); }

  1. COMMONHEADERBASIC 1(nodule)
  2. COMMONHEADERSHORT 7(ARRAY, BASETABLE, COUPLING, NODEREFLIST, nlist, NLIST, KAKEIZU, PSCLASS)
  3. COMMONHEADERPART 38(CARDLINK, MARGLINK, CARDTABLE, MARGTABLE, longtable, Bobject, DATALIST, LIST, REFLINK, PARTIALNAME, LINKTABLE, TOPOLOGY, FAMILYTREE, GENEBOX, GENELIST, PAIRBOX, …)
仮想関数
No. scope return value function name parameters process N/n
B/P
1 virtual nodule *& SLOT int edan NB
2 virtual char * classname void “NODULE” NB
3 virtual long getsize void size N
4 virtual char * dump char *buff NP
5 virtual char * Dump void N
6 pure virtual void Dispose void NP
7 virtual int getclasssize void sizeof(NODULE) NB
8 virtual int getSlots void vslotsize() N
9 virtual int CleanSansyo nodule *object refcnt NP
10 virtual int vslotsize void NB
11 virtual void print void N
12 virtual void clean void N
13 virtual void Clean void clean() NB#18
14 virtual void * dataPtr void (BPTR)this + sizeof(NODULE) NB
15 virtual long dataLen void NB
16 virtual void dataClean void memset(dataPtr(), 0, dataLen()) NB
17 virtual int Sansyo int slots, nodule *object=NULL n

QUICKDBを書き直して劇的に高速化

QUICKDBを大幅に書き直してKAKEIZU::getmarriageとgetcarddataでレコード全体を取り出すように修正したところ,データベースのロード処理が劇的に高速化した.これまではかなりの時間待たなくてはならなかったところ,ほとんど瞬時に完了するようになった!1万点を超えるサンプルをそこそこの時間で開けるようにしたいのだが,まだまだ手が届かない.ボトルネックになっているのはセグメント検定だが,それ以外では,ClearTableにかなりの時間を浪費している.

いや,その前にステージ7リンクの検証,ルックアップテーブルの生成にかなりの時間が掛かっている.ここでやっているのは,①CheckDataLink,②MakingLookUp,③NameSortだけだ.⇒どうも重いのは,③NameSortのようだ.改善の余地はあるだろうか?

53直系血族サンプルで,CheckDataLinkの所要時間は1.6秒,MakingLookUpは0秒,NameSortは344msだ.CheckDataLinkはZELファイルを読み込んだ場合ではパスしてもよい.Collatz4000X.ZELでは,NameSortで14.3秒掛かっている.⇒NameSortはlookup.tableを直接操作しているが,ループの中で前詰めを実行しているので,余分な手間が掛かっているのではないだろうか?書き直してみよう.

どうも,大した効果は得られなかった.13.953秒なので300msくらいしか短縮されなかった.いや,そもそも,どちらのサンプルもlookupには穴が空いていないので,前詰めによる時間浪費はミニマムなものでしかない.とりあえず,時間短縮するならNameSortをやらないようにするしかないだろう.これは仕様の問題であり,そう決めればよいというだけの話だ.⇒対処した.

ClearTableは後から見ることにして,三極検定,中でもセグメント検定の効率化が可能かどうかを見てみよう.⇒checkをオンにしただけなのだが,どこか壊してしまったのだろうか?動作がおかしくなってしまった.セグメント検定用のツールが壊れている.それにしても,checkを入れただけで副作用が発生するというのは考えづらい.⇒関数の実行に掛かる時間を測定・表示するために挿入したコードが誤動作していたようだ.GetTickCountで取り出した値はULONGLONGで,printfの書式の説明では%dでlong longを表示できるように書かれていたのだが… 誤動作を避けるため,引数でulong(xxx)で型変換するようにして収まった.

セグメント検定の実行ルーチンであるHorizontalSegmentは小さい方のサンプルでも600msくらい掛かっているが,その中から呼び出しているCheckMaximalSegmentが一回の呼び出しで200ms掛かり,それを3回実行しているので,HorizontalSegmentの実行時間のほとんどはCheckMaximalSegmentが費やしていることになる.従って,CheckMaximalSegmentの処理時間を短縮できれば,それだけHorizontalSegmentも軽くなるということになる.

しかし,その前に3回も実行する必要があるのかどうかもチェックしておく必要があるだろう.⇒3回を入口検査の1回だけに削減した.これだけでもかなり高速化した.トイレに入っている間に大きい方のサンプルが描画できるところまで来た.ここまで来ればそこそこ実用になる.大きいサンプルではHorizontalSegmentを1回実行するのに2.6秒くらい掛かっている.このうち,CheckMaximalSegmentが2.3~2.4秒で大半を占めている.CheckMaximalSegmentはもう少しダイエット可能だと思われるが,その前に系統並び替えに入ってから三極検定に移るまでに掛かっている時間コストを調べておこう.

系統並び替え全体では,サンプルA(1931点)で14秒,サンプルB(12243点)で316秒≒5分だ.まず,バックアップを取ってから始めよう.TOPOLOGY::ClearTableではlookupを使っていない.これはかなり不利だと思う.書き直してみよう.⇒57秒掛かった.修正前の時間を測っていないので,改善しているのかどうかは分からないが,ClearTableだけで1分というのはやはりかかり過ぎのような気がする.なぜだろう?結婚ではlookupを使わないことにしたのだが,かなり疎なテーブルになっている.結婚リンク数が1393のところ,テーブルは3578で3倍近くある.

確かに飛び番になっている.復活させるのはそれほど手は掛からないと思うが…サンプルBでは逆のことが起こっている.PDB,MDBとlookupのカウントが完全に同じだ.つまり,テーブルは最初から密に詰まっている.おそらく,これは以前保存するとき,基準ソートされた状態で保存していたのだろう.基準ソートするとテーブルは完全に前詰めされた状態になる.現在もそういう仕様になっているかどうかは分からない…いずれにせよ,サンプルBに関してはlookupを使っても効果はまったく出ないということになる.LINKTABLE:CheckIfMargValidという検査ルーチンはNOCHECKDATALINKで止めておこう.

もう少し,修正のフィックスを進めよう

もう少し,修正のフィックスを進めよう.

  1. UpdateDiagramProcを使う@20230212 → 確定
  2. getnewRefnumを使わない@20230201 → 確定
  3. emptyrecnを使わない@20230201 → 確定
  4. Renumberではリンクを移動しない@20230116 → 確定
  5. TOPOLOGY:GetRecordを使う@20230115 → 確定
  6. lookup未対応@20230110 → 確定
  7. MARGTABLEのlookupを廃止する@20230114 → 確定
  8. RenumberではUNDOをリセット@20230116 → 廃止
  9. checkMargPointの中でHeapBranchを実行している@20220415 → 確定
  10. REDLINEオーバーでループから脱出@20220420 → 確定

大体片が付いた.UpdateDiagramはかなり難があるので,原則として今後は使わないということにする.やはり,計算精度の問題があり,画面が崩れてしまう.系統並び替えを実施すればノーマルに表示できるので,ここでは時間効率より見た目を優先することにする.

データベースのロードに時間がかかり過ぎる.ファイルはバッファに読み込まれているが,単純なテキストの状態で放置されているため,毎回のシークに多大な時間を浪費しているように思われる.少なくとも,レコードを管理するテーブルのようなものが必要だ.⇒QUICKDB::LoadQuickを実装した.動作している.

InitLinkTableでは,KAKEIZU::getmarriageとgetcarddataでレコードを取り出している.この関数を直接書き直してやればよい.これらの関数では,各項目ごとにQUICKDBに要求を出してデータを受け取っているが,QUICKDB側でまとめて送り出してくるように書き換える必要がある.PICKコマンドを送信すると,DB側ではitemdata[]という配列に項目データを格納し,取り出しの準備を行うようになっている.